11

Is it possible to detect if the current version of C# is 6 or above with a preprocessor directive, so at compile time?

I want to do something like this:

var myVar = ...;
string name;

#if VERSION_6_OR_MORE
    name = nameof(myVar);
#else
    name = "myVar";
#endif

I use Visual Studio 2015 and C# 6, so I can use nameof(). Someone else wanting to compile this code however may be using an older version, where nameof() isn't present.

I want to use a preprocessor directive so I can keep the nameof() in C# 6, but someone else who doesn't use that version can compile it as well.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
A.Pissicat
  • 3,023
  • 4
  • 38
  • 93
  • 3
    Why do you want to do such thing? – Fka Oct 11 '16 at 09:21
  • Check out this answer: [How to get current Product Version in C#?](http://stackoverflow.com/a/7382420/6741868). – Keyur PATEL Oct 11 '16 at 09:22
  • @KeyurPATEL The OP needs the .NET Framework version - not the build version of his component. – Filburt Oct 11 '16 at 09:24
  • @Filburt I see, then would this help? [Is there an easy way to check the .NET Framework version?](http://stackoverflow.com/a/951915/6741868) – Keyur PATEL Oct 11 '16 at 09:25
  • Sounds like a typical [X/Y problem](http://meta.stackexchange.com/a/66378/143302) - use a dictionary instead of messing with reflection. – Filburt Oct 11 '16 at 09:27
  • I edited the question, I want pre processor instruction. I want to use `nameof` if I have C#6, else I want to use other way – A.Pissicat Oct 11 '16 at 09:28
  • 1
    Possible duplicate of [Detect target framework version at compile time](http://stackoverflow.com/questions/3436526/detect-target-framework-version-at-compile-time) – Kris Oct 11 '16 at 09:28
  • 1
    @Kris no, it's not a duplicate. Framework Version is not related to C# version. You can target .NET 2.0 with C# 6 and vice versa. – CodeCaster Oct 11 '16 at 09:31
  • 2
    imagine tracking down a bug that you colleague finds and you cannot reproduce when you have 50 of such blocks – slawekwin Oct 11 '16 at 09:38
  • @CodeCaster it was more about the creation of a "custom" constant than the meaning of that constant but I guess you have a point there. – Kris Oct 11 '16 at 09:45
  • @Kris agreed, that is _a_ solution, but then the question is: what information do you feed this constant with? Which isn't addressed in the duplicate, as that one is about .NET version and not C# version. :) – CodeCaster Oct 11 '16 at 09:45
  • @CodeCaster, i retracted my duplicate vote and already adressed that in my answer – Kris Oct 11 '16 at 09:46
  • Thanks you all for your comments, I will keep CodeCaster's solution, give up `nameof`. It's maybe possible to detect the version but in my case the maintenance will be hell. – A.Pissicat Oct 11 '16 at 09:50

5 Answers5

7

You can explicitly specify language version and define conditional constant in your project file:

<LangVersion>6</LangVersion>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants Condition="'$(LangVersion)' == '6'">DEBUG;TRACE;LANG_VERSION_6</DefineConstants>
6

The nameof() operator is meant to reduce maintenance: using it you don't have identifiers hidden in strings, so when you rename a variable, parameter or member, the place where you use it (for example an ArgumentNullException(nameof(paramName))) will be updated when you refactor the paramName name.

Now using your approach, you're effectively doubling your maintenance surface instead of reducing it (as you now have both the string variant and the nameof() version, both of which having to be maintained), negating the use of nameof() altogether.

So if you want to support older C# versions, stick to features that work in those versions: use identifiers in strings instead.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • You must be right, I prefer use `nameof`, it allows to rename variable without have to check all the strings depending on this variable. But if my colleagues can't update it will be hard to maintain. I will give up `nameof` then :-( – A.Pissicat Oct 11 '16 at 09:45
2

I am not aware of a pre-existing directive. But, here's what you can do:

  1. Define two build configurations: Say CSharp6 and CSharp5 (in the configuration manager).
  2. Select C6 from the configuration manger, and then go to the project properties. And define a symbol say CSHARP6.
  3. Change configurations and go to CSharp5
  4. Open project properties again and define a new symbol: CSHARP5

Then your directive conditional would look like:

#if CSHARP6
    name = nameof(myVar);
#else
    name = "myVar";
#endif

Delegate the selection of the configuration profile to the building agent, or as part of an instruction manual for compilation of the project.

Candide
  • 30,469
  • 8
  • 53
  • 60
1

The C# compiler doesn't let you define symbols with a numerical value. Any symbol is either defined or undefined, which means it's just a boolean.

Neither #define//define nor #if support any syntax other than boolean.

All C# versions so far are backwards-compatible with previous versions (except for some nuances like iterator variable semantics in foreach loops), so if you want to write code compatible with older versions, just don't use the newer features.

You could, define a symbol externally after detecting the available compiler version in your build script, but it's not worth the hassle IMO, as the lower-version C# code will be more legible and maintainable than duplicate blocks scattered around your code.

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
-1

Not sure about constants like that, but...

Something like typeof(int).Assembly.ImageRuntimeVersion could help out at runtime.

Additionally: You can create constants:

<DefineConstants Condition=" '$(LanguageVersion)' == 'v6.0' ">USING_CS6</DefineConstants>
<DefineConstants Condition=" '$(LanguageVersion)' != 'v6.0' ">NOT_USING_CS6</DefineConstants>

I have not tested this though.

Community
  • 1
  • 1
Kris
  • 40,604
  • 9
  • 72
  • 101