-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
We are considering a number of small changes to deconstruction, out variables, and pattern-matching to allow the use of wildcards where one could declare a variable, but the user has no intention of using the value captured in that variable. Here we discuss an open issue around name lookup, and propose a resolution.
The summary of this proposal is that the use of a wildcard has no effect on name lookup elsewhere in the code.
Original (LDM) approach
The original approach described by LDM was that the lookup of the identifier _ would treat it as a wildcard if nothing were found:
class Sample
{
void M()
{
M2(out _); // ok wildcard
}
}multiple declarations of the identifier _ in the same scope would cause _ to be considered a wildcard:
class Sample
{
void M()
{
int _ = e1;
int _ = e2;
M(out _); // ok, wildcard
}
}Existing cases, where the identifier is declared once, would still introduce a variable named _:
class Sample
{
void M(object o)
{
if (o is int _)
{
Console.WriteLine(_); //, uses declared pattern variable _
}
}
}Criticism of the this approach
This approach treats an unbound identifier _ as a wildcard, a single definition of _ as causing _ to bind to that definition, and multiple definitions again treating the use of _ as a wildcard. It requires careful definition of what "multiple definitions" are. Would a declaration of _ in an outer scope, and redeclaration in an inner scope cause a use to be a wildcard?
class Sample
{
void M(object o)
{
if (o is int _)
{
M(out int _); // allowed? wildcard?
}
}
}There are corresponding implementation difficulties that may make this approach unattractive.
Proposed alternative
The proposed alternative is that
- the declaration of an identifier
_in the following contexts would always be treated as a wildcard, and would have no effect on name lookup elsewhere:- an out argument,
- the left-hand-side of a deconstruction, or
- a pattern variable
- the use of the simple identifier
_as an expression as an out argument, or as a variable in the left-hand-side of a deconstruction, would be bound to a variable of that name. If no variable of that name is found, it is treated as a wildcard. This is similar to the treatment ofvaras a type.
class Sample
{
void M(object o)
{
if (o is int _) // wildcard by 1.
{
M(out int _); // wildcard by 1.
}
(int x, int _) = e; // wildcard by 1.
M(out _); // wildcard by 2.
(o, _) = e; // wildcard by 2.
(int x, _) = e; // wildcard by 2.
Console.WriteLine(_); // error: no _ in scope
}
}This is much simpler to implement and I believe much easier to explain.