Extension method invocations
In a method invocation (§7.5.5.1) of one of the forms
expr . identifier ( )
expr . identifier ( args )
expr . identifier < typeargs > ( )
expr . identifier < typeargs > ( args )
if the normal processing of the invocation finds no applicable methods, an attempt is made to process the construct as an extension method invocation. If expr or any of the args has compile-time type dynamic, extension methods will not apply.
The objective is to find the best type-name C, so that the corresponding static method invocation can take place:
C . identifier ( expr )
C . identifier ( expr , args )
C . identifier < typeargs > ( expr )
C . identifier < typeargs > ( expr , args )
An extension method Ci.Mj is eligible if:
Ci is a non-generic, non-nested class
The name of Mj is identifier
Mj is accessible and applicable when applied to the arguments as a static method as shown above
An implicit identity, reference or boxing conversion exists from expr to the type of the first parameter of Mj.
The search for C proceeds as follows:
Starting with the closest enclosing namespace declaration, continuing with each enclosing namespace declaration, and ending with the containing compilation unit, successive attempts are made to find a candidate set of extension methods:
If the given namespace or compilation unit directly contains non-generic type declarations Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.
If namespaces imported by using namespace directives in the given namespace or compilation unit directly contain non-generic type declarations Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.
If no candidate set is found in any enclosing namespace declaration or compilation unit, a compile-time error occurs.
Otherwise, overload resolution is applied to the candidate set as described in (§7.5.3). If no single best method is found, a compile-time error occurs.
C is the type within which the best method is declared as an extension method.
Using C as a target, the method call is then processed as a static method invocation (§7.5.4).
The preceding rules mean that instance methods take precedence over extension methods, that extension methods available in inner namespace declarations take precedence over extension methods available in outer namespace declarations, and that extension methods declared directly in a namespace take precedence over extension methods imported into that same namespace with a using namespace directive. For example:
public static class E
{
public static void F(this object obj, int i) { }
public static void F(this object obj, string s) { }
}
class A { }
class B
{
public void F(int i) { }
}
class C
{
public void F(object obj) { }
}
class X
{
static void Test(A a, B b, C c) {
a.F(1); // E.F(object, int)
a.F("hello"); // E.F(object, string)
b.F(1); // B.F(int)
b.F("hello"); // E.F(object, string)
c.F(1); // C.F(object)
c.F("hello"); // C.F(object)
}
}
In the example, B’s method takes precedence over the first extension method, and C’s method takes precedence over both extension methods.
public static class C
{
public static void F(this int i) { Console.WriteLine("C.F({0})", i); }
public static void G(this int i) { Console.WriteLine("C.G({0})", i); }
public static void H(this int i) { Console.WriteLine("C.H({0})", i); }
}
namespace N1
{
public static class D
{
public static void F(this int i) { Console.WriteLine("D.F({0})", i); }
public static void G(this int i) { Console.WriteLine("D.G({0})", i); }
}
}
namespace N2
{
using N1;
public static class E
{
public static void F(this int i) { Console.WriteLine("E.F({0})", i); }
}
class Test
{
static void Main(string[] args)
{
1.F();
2.G();
3.H();
}
}
}
The output of this example is:
E.F(1)
D.G(2)
C.H(3)
D.G takes precendece over C.G, and E.F takes precedence over both D.F and C.F.
Do'stlaringiz bilan baham: |