1

I know similar things have been asked and the answer usually was: don't do it.

See: Is there directive for c# language version, How to know the version of the C# using the preprocessor?

But since that is not an answer to the question, let me explain. My problem is that I'm trying to edit a Library (which is a cs project) that has to be able to be compiled with old language versions (not framework versions). The new code I added does not have to be usable by projects compiling with msbuild14.0. But the whole project still must compile on older C# versions (e.g. msbuild14.0) to not branch away for bugfixes etc.

So I want the file/class I added to be ignored by older C# compilers.

I can not change the .csproj of that library for my configuration since that would effect all solutions using that library.

Maybe there is a something like a solution wide #define? I mean I could do this (C# preprocessor define symbol based on environment variable) but it seems very hacky.

Jakob
  • 33
  • 6
  • 1
    I am not sure I understand. You care more for lang version than framework? How is that possible? And why do you maintain legacy versions in the same code? Wouldn't you have _at least_ different branches in your git (or whatever code management you are using)? – Fildor Jul 27 '23 at 14:07
  • Is it not possible to create a 2nd project/package that extends the old one? New client code can use the new variant, and old client code sticks to the old one. – Good Night Nerd Pride Jul 27 '23 at 14:11
  • Have you considered going NuGet? – Fildor Jul 27 '23 at 14:12
  • 1
    *"I want the file I added to be ignored by older C# compilers"* - How many different versions of the compiler are ***you*** going to use to compile this library? Once it's compiled, the language version isn't going to matter. What will matter is which framework versions are targeted due to the support needed by the language features used. C# is not like C++, where a library provides a header file to be included in the consumer's code and you have to account for different language versions used *there*. – madreflection Jul 27 '23 at 14:25
  • Well I have external constraints about how this has to be done. Your Ideas make sense but, but this is not up to me. I could create a new library but for the small addition I added that would be overkill. I might just have to make my code compatible to C# 5.0 – Jakob Jul 27 '23 at 14:27
  • 1
    I still don't get it. Why is that? There _must_ be something that's obvious to you, so you don't tell us that is totally throwing the rest of us off. – Fildor Jul 27 '23 at 14:30
  • @madreflection Yes, I know. The projects done in older VS versions will be compiled on a buildserver with a compiler of that version. (In a never touch a working system kind of manner). And all of the builds using that library will fail, when I use new language features. – Jakob Jul 27 '23 at 14:34
  • 1
    This screams X/Y problem to me. You're trying to solve a problem, but your question is about the solution you think you need, not the original problem you have. – madreflection Jul 27 '23 at 14:35
  • @Fildor-standswithMods I'm trying to tell you all I know. But I might be missing something ... – Jakob Jul 27 '23 at 14:36
  • You should probably checkout [multi platform targeting](https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting), i.e. `#if NET461 ... #elif WINDOWS_UWP ...`. I do not see how *language version* is relevant, since it does not need to match the platform. – JonasH Jul 27 '23 at 14:37
  • @madreflection Yes, the obvious solution is to make my code C# 5.0 compatible. Clean structural solutions, that let me use newer language features, would involve convincing a few people I guess – Jakob Jul 27 '23 at 14:39
  • 1
    *"all of the builds using that library will fail, when I use new language features"* - If the consumer is using newer language features, that doesn't affect the librray it's consuming. This sounds like "fear of future failure". If you have actual issues with this, it's definitely an X/Y problem. – madreflection Jul 27 '23 at 14:39
  • @madreflection yes, but this is about changing the library not the software consuming it. – Jakob Jul 27 '23 at 14:42
  • 1
    Yes that sounds like there is a need to modernize the whole toolchain. If you have a handcrafted build server, that only ever builds main, then of course it will use a certain compiler suite and expect the code to be compatible. That's why things like modern CI/CD Pipelines were invented, where you can explicitly configure the tools (and their versions), too and what branches to build and what to publish where... – Fildor Jul 27 '23 at 14:44
  • 1
    Again, what's going to matter here is which framework versions the library targets. Certain language features don't require newer runtime versions. Many features introduced in recent language versions can be used when compiling against any version of the runtime. The target-typed new (`new(...)`) syntax is one of them. Only use features that don't require runtime support and it won't break anything. – madreflection Jul 27 '23 at 14:48
  • 1
    Keep the library's target platform tightly controlled and features that require changing the target platform (like covariant returns, which absolutely requires .NET 5 at minimum) will involve compiler errors *in the library* itself, during development, before it ever gets to the build server. – madreflection Jul 27 '23 at 14:48
  • 1
    As JonasH mentioned, multi-targeting can benefit you. Certain language features require BCL support, which can be shimmed using NuGet packages, like [IsExternalInit](https://www.nuget.org/packages/IsExternalInit) for init-only properties, or [Nullable](https://www.nuget.org/packages/Nullable) for nullable reference types. When targeting older runtimes, where these supporting types don't exist, the packages fill the gap. Using conditional `PackageReference`s you can include them only for the target platform where they're needed. – madreflection Jul 27 '23 at 14:53
  • Yup I think, all of you are right there will be no clean enough solution without changing the tool chain. And yes I know that the Framework version should be the relevant thing. But I would have to change the Buildserver of a lot of legacy Projects (To compile with VS2019 etc.) and I am not in charge of these projects so I cannot. – Jakob Jul 27 '23 at 15:10
  • So I guess I can write an answer to this, which still is "Don't do it!"? – Jakob Jul 27 '23 at 15:12
  • Well, if you don't have the authority and/or clearance to change what needs to be changed to make it happen, then you don't have much of a choice, I'd say. – Fildor Jul 27 '23 at 15:15
  • What? No. There's no need to modify the tool chain. All you have to do is make sure you're using features that don't require newer runtime support, and add any external BCL support through NuGet packages for features that need that. If you post speicfics about which runtime version(s) you're targeting and which language features you want to use, and that specific combination is possible, you just might get an explanation of how to get it to work. – madreflection Jul 27 '23 at 15:22
  • This is more common than you might think https://stackoverflow.com/a/71417168/1690217 – Chris Schaller Jul 27 '23 at 22:38
  • @ChrisSchaller i don't see how that relates? pls explain. – Jakob Jul 28 '23 at 08:45
  • @madreflection what is external BCL Support? Link? – Jakob Jul 28 '23 at 08:45
  • How about putting my extending class in a subnamspace that is new and thus won't be imported by any legacy modules? Will `library` compile with msbuild14.0 if `library.subnamescpace` contains code that can't be compiled with msbuild14.0 but `library.subnamspace` is not imported anywhere? – Jakob Jul 28 '23 at 08:50
  • Ok, no it won't, tried it. – Jakob Jul 28 '23 at 09:06
  • By external BCL support, I'm referring to private implementations of types normally provided by the runtime that provide support for language features. They can be added as NuGet packages (already linked above as examples) if someone has taken the time to build one for a given feature. For example, `System.Runtime.CompilerServices.IsExternalInit` is a type that's used to mark a `set` accessor as an `init` accessor for the init-only properties feature in C# 9. The type was added in .NET 5, the runtime released concurrently with C# 9, to support the feature. – madreflection Jul 28 '23 at 13:09
  • However, if you want to use init-only properties with older runtimes, even back as far back as .NET Framework 2.0, you can add the `IsExternalInit` package (it works, I've tested it!), as long as the feature is used within the library or by any other project targeting C# 9 or higher. – madreflection Jul 28 '23 at 13:09
  • The C# compiler has a history of not needing the exact type in the exact assembly for supprting types, going all the way back to C# 3.0, when `System.Runtime.CompilerServices.ExtensionAttribute`, which is applied to extension methods (and the class and assembly that contain them), was added in .NET 3.5, but they made it possible to provide your own version of it in .NET 2.0 and still be able to use exension methods. – madreflection Jul 28 '23 at 13:10

0 Answers0