Можно ли условно скомпилировать версию .NET Framework?
Я могу вспомнить, что при работе с MFC вы могли поддерживать несколько версий фреймворка MFC, проверив макрос _MFC_VER
.
Сейчас я делаю кое-что с .NET 4 и хочу использовать Tuple в нескольких местах, но все равно сохраняю все, что совместимо с 3.5.
Я хочу сделать что-то вроде:
#if DOTNET4
public Tuple<TSource, TResult> SomeMethod<TSource, TResult>(){...}
#else
public KeyValuePair<TSource, TResult> SomeMethod<TSource, TResult>(){...}
#endif
Ответы
Ответ 1
Нет встроенных констант прекомпилятора, которые вы можете использовать. Но достаточно просто создать свои собственные конфигурации сборки в VS, причем каждая конфигурация имеет свой собственный набор определенных констант и, конечно же, целевую версию рамочной программы. Многие люди делают это, чтобы условно скомпилировать 32 или 64-разрядные различия.
Ответ 2
Существует одно большое предостережение при определении пользовательских символов компиляции в вашем .csproj(или .vbproj, теоретически): они перезаписывают все ранее определенные символы компиляции. Например, рассмотрим фрагмент MSBuild:
<PropertyGroup Condition="'$(TargetFrameworkVersion)' == 'v4.0'">
<DefineConstants>$(DefineConstants);DOTNET_40</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>ITS_CLOBBERING_TIME</DefineConstants>
</PropertyGroup>
Второй элемент DefineConstants, как показывает его значение, скроет первое значение DefineConstants. Чтобы этого избежать, вы захотите переписать второй элемент DefineConstants, чтобы он выглядел следующим образом:
<DefineConstants>$(DefineConstants);ITS_CLOBBERING_TIME</DefineConstants>
Кроме того, вы захотите разместить это внутри PropertyGroup, определенного после всех других PropertyGroups, так как Visual Studio 2010 добавляет в пользовательские символы компиляции таким образом, что он будет клонировать любые другие пользовательские символы компиляции, которые вы определяете, если они перед тем, как Visual Studio отбросит свое определение. Я подал эту проблему в Microsoft. Вы можете отслеживать его прогресс на Microsoft Connect.
Ответ 3
С другой стороны, ваш условный код компиляции может помешать программистам, которые его встречают.
Отредактировано на основе комментариев
Вероятно, лучше написать свой собственный класс, чтобы вы могли гарантировать, что он собирается делать, и у вас нет каких-либо странных проблем с сигнатурой или наследованием:
public class Pair<TSource, TResult>
{
public TSource Source { get; set; }
public TResult Result { get; set; }
public Pair() {}
public Pair(TSource source, TResult result)
{
Source = source;
Result = result;
}
// Perhaps override Equals() and GetHashCode() as well
}
Как всегда, полезно взвешивать, используя встроенный материал, и выкатывать свой собственный код. Обычно это означает, что вы спрашиваете себя: "Я поддерживаю и поддерживаю этот код?" vs. "Делает ли код то, что мне нужно, из коробки?"
В этом случае, поскольку у вас не будет Tuple<T1, T2>
, я просто напишу свой собственный простой, чтобы другие разработчики могли легко дышать:)
Ответ 4
Поскольку у вас должны быть разные проекты, вы можете иметь частичные классы и ссылаться только на тот, который вам нужен для каждого проекта, с конкретной логикой для них:
classname.cs public partial classname {...}
classname.40.cs public partial classname { public Tuple SomeMethod() {...} }
classname.35.cs public partial classname { public KeyValuePair SomeMethod() {...} }