What does the langversion switch do?

The C# compiler has a /langversion switch that is rarely used; most people do not know what it is for, and if asked to guess, most guess wrong. So let me disabuse you of your wrong guess right away: the purpose of the langversion flag is not to decide what C# language version to use. It is not a “go into compatibility mode” switch or a “use a previous version of the compiler” switch. The only effect of the langversion switch is to put the compiler in a mode in which use of features from a version of the language higher than the given version cause the compiler to emit an error.

Let me illustrate with a hilariously contrived example:

class C
{
    public static bool operator < (C c1, C c2) { return true; }
    public static bool operator > (C c1, C c2) { return true; }
    public static bool operator < (bool b1, C c2) { return true; }
    public static bool operator > (bool b1, C c2) { return true; }

    static C H = new C();
    static C I = new C();
    static C J = new C();
    static void G(bool b) { }
    static void Main()
    {
        G ( H < I > ( J ) );
    }
}

This ridiculous program compiles successfully in C# 1.0; it invokes the < operator on H and I, and then invokes the > operator on the resulting bool and J.

The C# 5.0 compiler rejects it because it parses the program as “call generic method H with type argument I and argument J”, and gives the errors I is a field but used as a type” and H cannot be used with type arguments”.

Specifying /langversion:ISO-1 does not tell the C# 5.0 compiler to go into a C# 1.0 compatibility mode and allow this. It still parses the call argument as a generic method invocation, and it instead gives the error “feature ‘generics’ cannot be used because it is not part of the ISO-1 C# language specification”. It does not even get as far as analyzing why the generics are wrong; it rejects any usage of generics out of hand. But the C# 5.0 parsing rules are still used, and those parsing rules say that this program uses generics. It does not fall back to the C# 1.0 parsing rules. Since generics were not a feature of C# 1.0, this is an error when that switch is specified.

So what is the purpose of the switch? Why is producing these errors desirable?

The purpose of the switch is to say “sanity check my program to make sure that I haven’t used a feature that requires the v5 compiler, because my coworker who hasn’t upgraded their machine is going to try to compile it with the v4 compiler”. The idea is that you can set this flag in your build system, so that some people can upgrade to C# 5 for testing, while the main body of the developers are still using C# 4. You want to make sure that the developers who are experimenting with C# 5 do not accidentally check in code that requires a feature that their coworkers cannot yet use.

People sometimes also ask me why the flag values are “ISO-1”, “ISO-2”, and then 3, 4, 5; what’s the “ISO” doing there and why is it not used consistently? The first two versions of C# were standardized by the ISO standardization body, but the more recent versions were not, so it’s a historical oddity. The original intention of the switch was I believe to tell you when you had used a feature in a way that was not strictly compliant with the ISO specification, which differs in a few details from the implementation. In practice, it just tells you when you are using a feature that was not present in the previous version.

17 thoughts on “What does the langversion switch do?

  1. That’s rather bizarre, but I’ve run into situations in other languages where this situation crippled an already sinking project due to bad team communication, and it would have been nice to have something like this. I’ll tuck this away into the Miscellaneous Features folder of my mind.

    • The benefits of standardization were deemed to be worth the cost for the first two versions, but not for any subsequent version.The design team doesn’t have to provide a justification for *not* spending more money!

      • Were there any plans to submit some possible future version for standardization, such that all of the current versions’ features would be covered by said standard?

  2. I’m perplexed. This is the kind of “nice” (but rarely used) feature I believed the C# team prefer not to implement (because the effort of being coded, documented, tested, published, maintained, etc.).

    • Sure, this is a “nice to have” but not absolutely crucial feature in a compiler. I do not know the history of the feature. Eric Gunnerson might remember what the pros and cons were.

      Also, note that this is a compiler feature, not a language feature. That changes the cost analysis somewhat.

  3. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1329

  4. You know, this is exactly the type of thing that happens naturally as a project grows organically as long as the .NET Framework has. You can’t see these types of things all the way down the road. When the cheese moves, you gotta move with it!

    Sure, many might think that this problem is easy to solve in practice? You could rewrite that code to fit the .NET 5.0 syntax I’m sure. But the problem is that if you’re Microsoft you **cannot make that assumption.** It has to be compatible somehow.

    To me this seems extremely straight forward and I’m glad I now have it in my arsenal. I personally haven’t run into it yet – but I’m sure I will faced with enough time. Fantastic implementation and very clear workaround to a much larger problem surrounding compatibility!

  5. Is there such a thing for framework version instead of language version? Let’s say that I have a .NET 3.5 compact framework project and a .NET 3.5 “regular” framework project. Can I compile my regular project and tell it: “Hey, can you check if this will also compile on the compact framework”?

    • Ah, maybe I misunderstood your question. What is cool about how .net works is that you can develop and compile an assembly against .the Compact Framework and still reference that CF assembly from a normal project. What I would do in your situation is just develop my assembly against the Compact Framework and never target the regular framework. That way, I would never accidentally use full framework-specific APIs and have to figure out how to port it back to the Compact Framework later. It’s still perfectly usable in full framework programs :-).

  6. Is it possible to somehow make Visual Studio 2012 compile with the old Visual Studio 2010 compiler?
    Because of the change in the foreach and the closure we now have different behaviors when code is compiled on different machines…

  7. > most people do not know what it is for, and if asked to guess, most guess wrong

    > sanity check my program to make sure that I haven’t used a feature that requires the v5 compiler, because my coworker who hasn’t upgraded their machine is going to try to compile it with the v4 compiler

    That’s exactly what I expected.

  8. I don’t think this feature is all that useful. It doesn’t even always do what it claims to do. For example, I can use auto-implemented get-only properties (from C#-6) even if I set /langversion:5.

  9. Pingback: How to change Visual Studio 2017 default language version for all projects? - Row Coding

Leave a comment