引言
在 MSDN 中对 System.Linq.Enumerable 类的 AsEnumerable 方法相关描述如下所示:
实际上,这个方法只有一条语句,就是原样返回它的唯一参数:
- reutrn source
使用 .NET Reflector 查看 .NET Framework 4.5 Class Library,就很清楚了:
测试程序
这种只是原样返回它的唯一参数的方法有什么用呢?让我们来看一个例子吧:
1 using System; 2 using System.Linq; 3 using System.Collections.Generic; 4 5 namespace Skyiv.Test 6 { 7 static class LinqTester 8 { 9 class Firster<T> : List<T> { public T First() { return default(T); } } 10 static void Test<T>(Firster<T> x) { Console.WriteLine("F:" + x.First()); } 11 static void Test<T>(List<T> x) { Console.WriteLine("L:" + x.First()); } 12 static void Test<T>(IEnumerable<T> x) { Console.WriteLine("I:" + x.First()); } 13 14 static void Main() 15 { 16 var a = new Firster<int> { 2, 3, 5 }; 17 Test(a); 18 Test(a as List<int>); 19 Test(a.AsEnumerable()); 20 } 21 } 22 }
在上述程序中:
- Firster<T> 类是 List<T> 类的派生类。
- List<T> 类实现了 IEnumerable<T> 接口。
- IEnumerable<T> 接口有一个 First 扩展方法 (定义在 System.Linq.Enumerable 类中)。
- Firster<T> 类定义了一个 First 实例方法。
在 Arch Linux 的 Mono 环境下编译和运行:
work$ dmcs LinqTester.cs && mono LinqTester.exe F:0 L:2 I:2
上述运行结果解释如下:
- 第 17 行调用第 10 行的 Test 方法,参数类型是 Firster<T>,于是调用 Firster<T> 类的 First 实例方法,输出: F:0。
- 第 18 行调用第 11 行的 Test 方法,参数类型是 List<T>,在 List<T> 类中没有找到 First 方法,由于 List<T> 类实现了 IEnumerable<T> 接口,所以调用 IEnumerable<T> 接口的 First 扩展方法,输出: L:2。
- 第 19 行调用第 12 行的 Test 方法,参数类型是 IEnumerable<T>,于是调用 IEnumerable<T> 接口的 First 扩展方法,输出: I:2。
如果在上述程序中,分别进行以下操作:
- 删除第 10 行的语句
- 删除第 11 行的语句
- 删除第 10 行和第 11 行的语句
再重新编译和运行,分别会有什么结果呢?
另外一个测试程序
前面的测试程序引用了 System.Linq 和 System.Collections.Generic 命名空间,涉及到的东东比较复杂。下面我们来一个简单点的测试程序:
1 using System; 2 3 namespace Skyiv.Test 4 { 5 class Thing { public string GetId() { return "Thing"; } } 6 7 static class Extensions 8 { 9 public static object AsObject(this object x) { return x; } 10 public static string GetId(this object x) { return "Object"; } 11 } 12 13 static class Tester 14 { 15 static void Main() 16 { 17 var a = new Thing(); 18 var b = a.AsObject(); 19 Console.WriteLine(a.GetId()); 20 Console.WriteLine(b.GetId()); 21 } 22 } 23 }
在上述程序中:
- Thing 类定义了一个 GetId 实例方法。
- Extensions 类为 object 类定义了一个 GetId 扩展方法。
- Extensions 类为 object 类定义了一个 AsObject 扩展方法。
对于上述程序来说,第 18 行的语句使用下面任何一个都是等效的:
- var b = a.AsObject();
- var b = (object)a;
- object b = a;
在 Arch Linux 操作系统的 Mono 环境中编译和运行:
work$ dmcs Tester.cs && mono Tester.exe Thing Object
上述运行结果解释如下:
- 第 19 行调用了定义在第 5 行的 GetId 实例方法,输出: Thing 。
- 第 20 行调用了定义在第 10 行的 GetId 扩展方法,输出: Object 。
使用 monodis 反汇编 Tester.exe,得到 Main 方法的 MSIL 代码如下所示:
1 .namespace Skyiv.Test 2 { 3 .class private auto ansi abstract sealed beforefieldinit Tester extends [mscorlib]System.Object 4 { 5 // method line 5 6 .method private static hidebysig default void Main () cil managed 7 { 8 // Method begins at RVA 0x2108 9 .entrypoint 10 // Code size 36 (0x24) 11 .maxstack 7 12 .locals init ( 13 class Skyiv.Test.Thing V_0, object V_1) 14 IL_0000: newobj instance void class Skyiv.Test.Thing::'.ctor'() 15 IL_0005: stloc.0 16 IL_0006: ldloc.0 17 IL_0007: call object class Skyiv.Test.Extensions::AsObject(object) 18 IL_000c: stloc.1 19 IL_000d: ldloc.0 20 IL_000e: callvirt instance string class Skyiv.Test.Thing::GetId() 21 IL_0013: call void class [mscorlib]System.Console::WriteLine(string) 22 IL_0018: ldloc.1 23 IL_0019: call string class Skyiv.Test.Extensions::GetId(object) 24 IL_001e: call void class [mscorlib]System.Console::WriteLine(string) 25 IL_0023: ret 26 } // end of method Tester::Main 27 } // end of class Skyiv.Test.Tester 28 }
上述 MSIL 代码中:
- 第 20 行对应 C# 程序第 19 行的 GetId 实例方法调用 (使用 callvirt 指令)。
- 第 23 行对应 C# 程序第 20 行的 GetId 扩展方法调用 (使用 call 指令)。