Replies: 17 comments
-
This could affect method resolution: public enum E1 { Blue }
public enum E2 { Blue }
public class C
{
public static void M(E1) { }
public static void M(E2) { }
}
C.M(Blue); // which one? Obviously above example is an ambiguous reference like any other, but the point is that introducing the feature could change the set of method overloads that are being considered in existing code... What would be the typing experience? Do you expect the autocompletion list to contain values from all applicable enums until the correct one is resolved? Changing a method's argument type of enum to another enum now results in compilation errors, but this feature would silently allow it if the used enum values had the same name. (And I suspect it might allow some bugs to be easily overloooked when you don't see the type name, but that is subjective.) |
Beta Was this translation helpful? Give feedback.
-
It would only work when unambiguous. Otherwise you'd be required to explicitly qualify the enum member using current syntax. And any currently legal syntax would take precedence so existing code would continue to compile with the same behavior. |
Beta Was this translation helpful? Give feedback.
-
Most of the inconveniences overcome by this are overcome by using But it would be nice to have an implicit |
Beta Was this translation helpful? Give feedback.
-
It doesn't work like that, I guess. When the compiler looks up an identifier, and doesn't find anything, it will resolve it as an enum member based on the target type (if any). So |
Beta Was this translation helpful? Give feedback.
-
No, it only affects code where the simple name fails ordinary name lookup. In other words, it only affects code that is an error today. |
Beta Was this translation helpful? Give feedback.
-
@alrz, what is an |
Beta Was this translation helpful? Give feedback.
-
Effectively, any enum type imported with Then this proposal adds the following, in order of precedence:
As a point of clarification: this proposal does not import members unless the enum type is already imported in the existing release? In other words, EDIT: removed a question regarding the second item above, establishing precedence between those items is needed for overload resolution |
Beta Was this translation helpful? Give feedback.
-
The difference is that any enum member is of the type of the enum itself. So extending it to all static members would only include those that return an object of the containing type which is rarely useful. No, |
Beta Was this translation helpful? Give feedback.
-
Open question: in the following, what is the expected behavior? enum X { Foo }
enum Y { Foo }
void M(X x) {}
void M(Y y) {}
M(Foo);
Alternatively we can restrict the feature to places with a single target type, e.g. assignments etc. |
Beta Was this translation helpful? Give feedback.
-
Another approach instead of target typing is to lookup enum members of enums that are already in the scope (similar to how we look for extension methods). That would make the above example a CS0229 and the following legal. enum X { Foo }
class C {
void M() {
var x = Foo; // X.Foo
}
} Note: we can require an identity or nullable conversion between enum and target type to make it more predictable. Furthermore, this could be mixed with target typing to disambiguate, similar to the following using static Z;
using static V;
class Z {
public static void M1(int x){}
}
class V {
public static void M1(){}
}
class C {
static void M(Action x) {
M(M1);
}
} Currently we only do this for method groups. Same example with enums gives CS0229. PS: if you mix enum and method groups, the latter simply wins, I'd expected an ambigiouty error though. |
Beta Was this translation helpful? Give feedback.
-
I would expect the same behavior as if |
Beta Was this translation helpful? Give feedback.
-
I'd prefer that one too -- but that makes it a lookup error, not a target-type ambiguity (unlike what the title suggests). However, I think we still can use target-typing for disambiguation (that's probably another proposal on its own). |
Beta Was this translation helpful? Give feedback.
-
That wouldn't be true if you have enums in different namespaces, e.g. using N;
class C
{
void M()
{
var x = Foo; // gloabl::E.Foo;
}
}
enum E { Foo }
namespace N
{
enum E { Foo }
} This is the same behavior as extension methods. |
Beta Was this translation helpful? Give feedback.
-
If you add the |
Beta Was this translation helpful? Give feedback.
-
In the example above both using static E;
using static N.E; Which makes Top-level names are considered "closer", for the same reason |
Beta Was this translation helpful? Give feedback.
-
The only remaining issue that I can think of, is that how we resolve enum member name vs method groups if both are in the scope. Should we prefer the method group (as in dotnet/roslyn#20605) or should we consider target-typing to resolve the appropriate member (same for enum vs enum) which makes certain situations ambiguous? |
Beta Was this translation helpful? Give feedback.
-
This is now championed at #2926 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
@markrendle commented on Sat Feb 28 2015
There are probably dozens of reasons why this wouldn't work, but I was just looking at an API with a method
ListContainers(bool)
(it was in Go) and thinking about enums being better than Boolean flags, when it occurred to me that in .NET, expressing enums can be verbose.In cases where the type can be determined from argument, property or field type, why does one have to type the enum name? Would it not be easier and more readable to just use the enum member name, like
type.GetProperties(Public | Static)
?This is bound to be (a) stupid because of obvious reasons and also (b) already possible in F#, so I apologise in advance for wasting everybody's time.
@ashmind commented on Sat Feb 28 2015
This is something I wanted for a while (one way or another), but wouldn't static imports cover most of the use cases? Sure they aren't as neat since you need an import, but they do not need any other new features to be added.
@gafter commented on Sat Feb 28 2015
@markrendle This already works with static import, and there is at least one source file in the Roslyn compiler that uses it extensively.
@paulomorgado commented on Mon Mar 02 2015
@gafter, I kind of like @markrendle's proposal. This would equate to have enums implicitly staticaly imported when the value is known to be of an enum type. This would be scoped to that expression instead of the whole file.
@gafter commented on Mon Mar 02 2015
How would we do this without it being a breaking change?
@paulomorgado commented on Mon Mar 02 2015
I haven't given that much thought, but I suppose it's the same as with static imports. Only that the static import is only in effect at that expression that as a type of the enum.
Imangine this:
What I'm sayning is that this would work just the same if
using static Enums.SomeEnum;
was not there.Whatever is not valid with static imports, is not valid here.
@gafter commented on Tue Mar 03 2015
@paulomorgado With static import, the meaning of your code doesn't change unless you change your source code. I'm concerned that your proposal could change the meaning of existing code when the source has not changed. I'd need to see the precise name lookup rules you propose to know.
@markrendle commented on Tue Mar 03 2015
Hi, sorry, been unable to post for a few days so couldn't respond.
The issue with
using static
is that it's pretty common for multiple enums in a particular area to have common member names; for exampleBindingFlags
andFieldAttributeFlags
both havePublic
(I like this example because having half a dozen verboseBindingFlags
values OR'd together really clutters up aGetProperties
call). I'm not sure what the rules are for having twousing static
declarations with conflicting member names?As far as changing the meaning of existing code, there's obviously the possibility of introducing ambiguity where there is a member in scope with the same name as an enum member, either a property or constant of the same type, or a member of a type for which an overload of the method being called is also available. If those ambiguities caused compiler errors where previously there were none then obviously that's bad. If those ambiguities were resolved by defaulting to the original behaviour, and requiring explicit enum qualification, then I don't know how that shakes out.
@paulomorgado commented on Tue Mar 03 2015
@gafter, it's @markrendle proposal, not mine. I'm just supporting it.
This might make code, that otherwise wouldn't compile, compile. But I don't think it would break any code that compiles now.
But I do understand your caution. Importing statics will involve explicitly changing the code while this wouldn't.
@gafter commented on Tue Mar 03 2015
OK, I'll reopen this so it can be considered as a separate approach for handling enums.
@TedDriggs commented on Wed Mar 04 2015
This still seems brittle to me. Consider the following case, where SomeClass has a constructor that accepts a
Color
:Now I add a new property to my class:
At this point, the behavior of
DoSomething
has silently changed from passingColor.Red
to passingOkay.Red
. There is no warning or error: Both snippets are valid under the proposed syntax. This particular example is contrived, but I could easily see this becoming an issue with inheritance, partial classes, or properties imported viausing static
@chrisaut commented on Wed Mar 04 2015
Wouldn't the same problem occur with that code if there was a using static System. Color? Meaning of the code silently changing?
@gafter commented on Wed Mar 04 2015
@markrendle Can you tell me what changes you are proposing to the name lookup rules? In the case of a method call, one does not have a target type until the proper overload has been resolved, but we need to bind the arguments in order to resolve the overload. I don't see how to specify (or implement) the feature requested here.
@paulomorgado commented on Wed Mar 04 2015
@TedDriggs, you changed the code and caused it. What @gafter is concerned about is unchanged compilable code where some the compiler will now assume it's an enum member where it wasn't before.
As @chrisaut stated, if you use static imports, you'll have the same problem.
@paulomorgado commented on Wed Mar 04 2015
@gafter, it's more complicated than this but let me try.
It's a tall order...
What if this would only happen if the parameter name was specified?
@gafter commented on Wed Mar 04 2015
@paulomorgado I think that could work... what if the name is a member of more than one enum type? Ambiguity error?
@gafter commented on Wed Mar 04 2015
Having the parameter name specified shouldn't make any difference, as it could be the name of a parameter in more than one method.
@markrendle commented on Wed Mar 04 2015
@gafter I'm afraid I don't know enough about name lookup rules to answer that, but what @paulomorgado said sounds right to me :)
Obviously
var x = UnqualifiedMemberName
wouldn't work; this is similar tovar f = MethodName
not being valid. ButBindingFlags x = Public
would work.@paulomorgado commented on Wed Mar 04 2015
@gafter, this would work just the same as static imports but only scoped to the method call.
In order for this to work, all enums in scope would have to be imported. And that can be a lot.
@judemelancon commented on Mon Mar 09 2015
The concern raised by @TedDriggs strikes me as significant enough to merit being addressed more thoroughly. (I will proceed from the assumption he meant
ConsoleColor
for his example, as it is an enum.)First off, I must admit I find this change quite aesthetically pleasant, and it really would make some code much less verbose. As such, it pains me to say that this feature is a spooky action at a distance nightmare. Adding any field, property, local, or parameter with the same name as any enumeration constant (even in a different assembly) can silently change the meaning of code where both are in scope. Unless the compiler is going to warn for every field, property, local, or parameter in scope at the same time as an enum value with the same name, it will be quite transparent to the programmer, as well. Even if such a warning were implemented, the sad truth is that many ignore warnings, and it would be likely so noisy as to further encourage that.
The analogous situation with a static import has the directive there to see at debugging time. Furthermore, it's a choice to use that feature.
@TedDriggs brought all of the following scenarios up, but I feel they are serious enough to bear reiterating:
Imagine a currently existing FooLibrary (or a code generation tool) that added such a property between versions 1.1 and 1.2. This wasn't a breaking change, so it wouldn't make such a list if it were kept. Imagine code using FooLibrary 1.1 that moves to a version of C# including this feature. Now upgrading FooLibrary to 1.2 can break that code. This feature would retroactively make any change from the list above breaking for library code.
@gafter commented on Tue Nov 17 2015
We're considering this for certain contexts, such as the branches of a switch. See #6739 for related discussion.
@HaloFour commented on Tue Nov 17 2015
@gafter I hope you didn't help Sun take out a patent on that one. 😀
@HaloFour commented on Tue Nov 17 2015
@gafter
I assume that the meaning of the following would not change. Could be confusing, but probably not a common scenario:
@gafter commented on Tue Nov 17 2015
@HaloFour Right, it would not change. That is the reason for the words "otherwise unresolvable" in the title. It only tries this lookup if the rules from the previous version of the language fail to find something.
@bondsbw commented on Mon Feb 01 2016
@TedDriggs @judemelancon
I'm not sure this actually represents a breaking change to working code, if you assume that enum member resolution has lower precedence than the others. Anything that did compile would compile the same, and any cases where the enums could be resolved according to this proposal would not have compiled previously.
Perhaps a compiler warning is warranted.
Another assumption is that, like today, enum member resolution would not allow implicit conversions from one type of enum to another. Resolution would need to consider only the target enum type.
Beta Was this translation helpful? Give feedback.
All reactions