Можно ли условно скомпилировать версию .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() {...}   }