Можно ли условно скомпилировать в версию.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
4 ответа
Нет встроенных констант прекомпилятора, которые вы можете использовать. Но достаточно легко создать свои собственные конфигурации сборки в VS, каждая из которых имеет свой собственный набор определенных констант и, конечно, целевую версию фреймворка. Многие люди делают это для условной компиляции на основе 32- или 64-битных различий.
Если вы используете систему сборки.NET Core, вы можете использовать ее предопределенные символы:
#if NET40
public Tuple<TSource, TResult> SomeMethod<TSource, TResult>(){...}
#else
public KeyValuePair<TSource, TResult> SomeMethod<TSource, TResult>(){...}
#endif
Список предопределенных символов задокументирован в разделе Разработка библиотек с помощью кроссплатформенных инструментов и #if (Справочник по C#):
.NET Framework:
NETFRAMEWORK
,NET20
,NET35
,NET40
,NET45
,NET451
,NET452
,NET46
,NET461
,NET462
,NET47
,NET471
,NET472
,NET48
Стандарт.NET:
NETSTANDARD
,NETSTANDARD1_0
,NETSTANDARD1_1
,NETSTANDARD1_2
,NETSTANDARD1_3
,NETSTANDARD1_4
,NETSTANDARD1_5
,NETSTANDARD1_6
,NETSTANDARD2_0
,NETSTANDARD2_1
.NET Core:
NETCOREAPP
,NETCOREAPP1_0
,NETCOREAPP1_1
,NETCOREAPP2_0
,NETCOREAPP2_1
,NETCOREAPP2_2
,NETCOREAPP3_0
При определении пользовательских символов компиляции в вашем.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 Студия снимает свое определение. Я подал эту проблему в Microsoft. Вы можете отслеживать его прогресс в Microsoft Connect.
Кстати, ваш код условной компиляции разочарует программистов, которые с ним сталкиваются.
Отредактировано на основе комментариев
Вероятно, лучше написать свой собственный класс, чтобы вы могли гарантировать, что он собирается делать, и у вас нет никаких странных проблем с сигнатурой или наследованием:
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>
Я бы просто написал свой простой, чтобы другие разработчики могли дышать легко:)
Поскольку у вас должны быть разные проекты, вы можете иметь частичные классы и ссылаться только на те, которые вам нужны для каждого проекта, со специальной логикой для них:
classname.cs публичное частичное имя класса {...}
classname.40.cs публичное частичное имя класса {public Tuple SomeMethod () {...}}
classname.35.cs публичное частичное имя класса { public KeyValuePair SomeMethod(){...} }