Preprocessor directives to check for existence of types and members #4204
Replies: 6 comments 8 replies
-
I believe for this proposal to be implemented, a lot about how the compiler works would have to be changed. To my knowledge, normal #if directives are resolved during lexical analysis. At that point, the compiler doesn't know anything about types, properties, methods, etc. For this to work, it would need to carry theses directives until semantical analysis. I don't think it's impossible, but the cost would probably be immense for pretty little gain. |
Beta Was this translation helpful? Give feedback.
-
The languages that I think of that do this, e.g. JavaScript, do the evaluation at runtime. With the evaluation at compile time this seems like it would only offer little gain since the type/member might not exist at runtime. I do get what it's trying to do, but having the existence of any type/member be a flag vs. having a set number of explicit flags only feels like it would make things more complicated, not less. |
Beta Was this translation helpful? Give feedback.
-
Of note, this is why the BCL team is starting to add |
Beta Was this translation helpful? Give feedback.
-
The chicken-and-egg problem@Phantonia wrote:
Good point! I thought about how to solve the significant chicken-and-egg problem you mentioned, and I came up with this: For example, if your app references/uses .NET Framework, then your app could use Additionally, it would only work with fully-qualified names. C# @333fred wrote:
Great! I'm glad they're doing that. I think they should proceed with that, although it doesn't completely solve the problem, considering that actually the recommended technique is to check for the existence of specific features instead of checking version numbers. |
Beta Was this translation helpful? Give feedback.
-
I agree with @333fred -- we're adding support to allowing pre-processor version checks that can do In practice, hit testing individual APIs feels extremely noisy to me. And considering the need for using fully qualified names also quite ugly. To me, version checks -- while not ideal -- are the more pragmatic solution. Even for cases like WinRT, where you can do hit testing on individual symbol, we're moving to a version based model that is paired with an analyzer to ensure developers check for the correct version. |
Beta Was this translation helpful? Give feedback.
-
For what its worth, the only static/compiled language I know of that has this kind of feature is Swift, which does it based on OS version and not each individual method: https://nshipster.com/available/#available-1 In other languages such as Javascript, Objective-C, etc. this is only possible as a runtime check, which is what I believe Swift actually compiles .NET offers something very similar with the new platform compatibility attributes (https://docs.microsoft.com/en-us/dotnet/standard/analyzers/platform-compat-analyzer), but due to the nature of .NET this approach only works for APIs that you already have available, just that they would throw an exception on an unsupported platform. You can't use this to gate against APIs of .NET itself. Due to the differences in how the SDKs work, though, I don't think its feasible to take the Swift approach here, and I can't think of anything else that would achieve the same effect... other than |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I suggest enhancing the conditional compilation directives (AKA preprocessor directives) to support compile-time checking of the existence of types and members, instead of the common-but-problematic technique of checking for particular version numbers of .NET Framework etc.
To check if a class/struct/interface/enum/delegate/type exists, I suggest:
To check if a generic type exists:
To check if a property exists:
Alternatively, if you prefer:
To check for a property in a generic type:
To check if an event exists:
To check if a field exists:
To check if a method exists:
MethodExists
would check for a particular overload of the method, thus specifying the method signature (parameter types) would be required. The parameter names would be optional, but if the names are specified, thenMethodExists
would only return true if the parameter names exactly match, thereby ensuring that the correct method is truly detected.To check if a particular constructor method exists:
To check if a particular member of an enum exists:
To check if a namespace exists:
The fact that
#if x
is an expression would remain unchanged, thus this example would be valid:These new conditional compilation functions (
TypeExists
etc) would eliminate the frequent problem of incorrectly used symbols. I often see incorrect usage of conditional compilation symbols, for example:This problem is especially common because, unlike C++, C# only allows a conditional compilation symbol to have a boolean value not an integer. Thus C++ but not C# supports:
However, even if the above version comparison were supported, it's still not the right way. Microsoft recommends to check for specific features, not to check for specific version numbers of the OS or .NET Framework etc. However often we're forced to check for specific version numbers anyway because no better solution exists. The suggested new conditional compilation functions (
TypeExists
etc) would allow us to test for the existence of specific classes etc instead of the bad way of checking for specific version numbers.By the way, it's great to see how the staff have improved the way that C# language ideas and proposals are posted in GitHub. In my opinion, this was a very necessary improvement to procedures, so I'm really glad to see it has now been done. This new way should help reduce the problem that existed previously; the problem where people posted ideas and then received pseudo-inappropriate replies from a small handful of hardcore participants who were not even staff members yet talked with such authority that people were misled into thinking that they were discussing with staff when in reality they were discussing with merely peers not staff. The small handful of hardcore participants was very offputting. It's great to see that staff have already taken steps to solve this problem.
For example, someone would post an idea, just an idea, and then a hardcore participant would very nearly/practically reply like:
And then the member of the public was left wondering:
Hardcore fans destroy or damage the object of their admiration. But that's the past! It's now great to see that staff implemented changes to procedures to solve the problem of a small handful of hardcore participants/fans driving away most other "normal" participants.
The chicken-and-egg problem
(Edited. I added this after @Phantonia raised a very good point.)
To solve the chicken-and-egg problem of determining whether a type or member exists while simultaneously compiling or analyzing the same source code, it would be reasonable to solve this by saying that
#if TypeExists(x)
etc will only work with external/referenced Assemblies/DLL's/metadata, not with the same code as currently being compiled.For example, if your app references/uses .NET Framework, then your app could use
#if TypeExists(x)
to check if typex
exists in .NET Framework or any other Assembly that your app references/uses, but your app cannot use it to check if typex
exists within your app.Additionally, it would only work with fully-qualified names. C#
using
statements would have no effect on these new#if
directives. Thus you could write#if TypeExists(System.Text.Rune)
but you could not write#if TypeExists(Rune)
regardless of whether you wroteusing System.Text;
anywhere in the .cs file.Beta Was this translation helpful? Give feedback.
All reactions