-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
There are three contexts in which lookup of a simple (not qualified) name would benefit from the fallback of looking up the identifier in a target type.
Simple ID expression
When an expression is an unqualified identifier, and lookup fails to find any accessible binding for that identifier, it is treated as target-typed. When the target type becomes available, the identifier is looked up in the context of the target type. If the identifier in that type designates an accessible constant, static field, or static property whose type is that target type, the identifier binds to that member.
Example:
enum Color { Red, Greed }
Color M1() => Red; // OK, binds to Color.Red
int IsRed(Color c) => c switch {
Red => true, // OK, binds to Color.Red
_ => false,
};
Object creation expression
When an object creation expression's type is given as an unqualified identifier (possibly with type arguments), and lookup fails to find any accessible binding for that identifier, it is treated as target-typed. When the target type becomes available, the identifier is looked up in the context of the target type (and the rest of the creation expression is bound, like the target-typed new expression). If the identifier in that type designates an accessible type that has an implicit reference conversion to that target type, the identifier binds to that type.
Example:
class Outer
{
public class Inner1: Outer { }
public class Inner2<T>: Outer { }
}
Outer M1() => new Inner1(); // binds to Outer.Inner1
Outer M2() => new Inner2<int>(); // binds to Outer.Inner2<T>
Type in a pattern
When the type appearing in a pattern is given as an unqualified identifier (possibly with type arguments), and lookup fails to find any accessible binding for that identifier, the identifier is looked up in the context of the pattern's input type. If the identifier in that type designates an accessible type, the identifier binds to that accessible type.
Example:
class Outer
{
public class Inner1: Outer { }
public class Inner2<T>: Outer { }
}
int M1(Outer o) => o switch
{
Inner1 => 1, // ok, binds to Outer.Inner1
Inner2<int> => 2, // ok, binds to Outer.Inner2<T>
_ => 3,
};
bool M2(Outer o) => o is Inner1; // ok, binds to Outer.Inner1
bool M3(Outer o) => o is Inner2<int>; // ok, binds to Outer.Inner2<T>