C#3.0新特性:扩展方法初探

www.chinacs.net  2006-03-26  中文C#技术站

  C#3.0中一个激动人心的特性就是扩展方法:你可以使用实例方法的语法来调用静态方法。本文仔细阐述了这一新特性并且给出了几个相应的例子。

  声明扩展方法

  扩展方法的行为和静态方法是非常类似的,你只能在静态类中声明它们。为声明一个扩展方法,你需要给该方法的第一个参数指定this关键字,如下例:

// Program.cs
public static class EMClass
{
 public static int ToInt32Ext(this string s)
 {
  return Int32.Parse(s);
 }
 public static int ToInt32Static(string s)
 {
  return Int32.Parse(s);
 }
}
class Program
{
 static void Main(string[] args)
 {
  string s = "9";
  int i = s.ToInt32Ext(); // LINE A
  Console.WriteLine(i);
  int j = EMClass.ToInt32Static(s); // LINE B
  Console.WriteLine(j);
  Console.ReadLine();
 }
}

  为编译如上代码,你需要安装Visual Studio 2005和LINQ的预览版。如果你已经安装了VS2005,那么你将在Visual C#的LINQ Preview里看到三个新的工程模板:LINQ命令行应用程序,LINQ窗口程序和LINQ库。如下操作编译代码:

  1. 打开VS2005编辑器,创建一个新工程,在新建工程窗口中选择LINQ Console作为工程模板。

  2. 将工程命名为ExtensionMethods,点击Ok。

  3. 将如上代码键入编辑器。

  4. 按下F5编译工程并运行。

  如果你只是安装了.NET 2.0,那么你可以运行命令行编译器:

Csc.exe /reference:"C:\Program Files\LINQ Preview\Bin
\System.Data.DLINQ.dll"
/reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll
/reference:"C:\Program Files\LINQ Preview\Bin\System.Query.dll"
/reference:"C:\Program Files\LINQ Preview\Bin\System.Xml.XLINQ.dll"
/target:exe Program.cs

  就像你在如上代码里所看到的那样,扩展方法(ToInt32Ext)和普通的静态方法(ToInt32Static)的不同在于:

  1. 扩展方法的第一个参数有一个this关键字,而静态方法不会在它的参数声明里有this关键字。

  2. 当使用扩展方法的是哦户,使用this关键字声明的的参数没有进行传递。在上面的例子里,Line A就是一个使用扩展方法ToInt32Ext的例子。不需要将参数传递给它。当静态方法在使用的时候,是不能忽略掉任何的参数的。所有的参数必须传递进入函数。Line B就是一个例子。

  3. 扩展方法只能在静态类中定义。对于静态方法,这并不成为一个要求,因为静态方法可以在一个静态类或普通类中存在。

  4. 扩展方法只能针对实例调用。

  扩展方法,尽管本质上还是静态的,但是只能针对实例调用。如果在一个类中调用它们将会引发编译错误。调用它们的类实例是由声明中的第一个参数决定的,就是有关键字this修饰的那个。

  在IL内部

  如果你观看IL里对以上代码的分析结果,你将会看到如下图的结果:


  以下是IL对于扩展方法ToInt32Ext的分析:

.method public hidebysig static int32 ToInt32Ext(string s) cil managed
{
 .custom instance void [System.Query]System.Runtime
 .CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
 // Code size 12 (0xc)
 .maxstack 1
 .locals init ([0] int32 CSCODE_REPLACEMENT 200)
 IL_0000: nop
 IL_0001: ldarg.0
 IL_0002: call int32 [mscorlib]System.Int32::Parse(string)
 IL_0007: stloc.0
 IL_0008: br.s IL_000a
 IL_000a: ldloc.0
 IL_000b: ret
} // end of method EMClass::ToInt32Ext

以下代码是IL对静态方法ToInt32Static的分析:

.method public hidebysig static int32 ToInt32Static(string s) cil managed
{
 // Code size 12 (0xc)
 .maxstack 1
 .locals init ([0] int32 CSCODE_REPLACEMENT 300)
 IL_0000: nop
 IL_0001: ldarg.0
 IL_0002: call int32 [mscorlib]System.Int32::Parse(string)
 IL_0007: stloc.0
 IL_0008: br.s IL_000a
 IL_000a: ldloc.0
 IL_000b: ret
} // end of method EMClass::ToInt32Static

  .custom instance void: 本行代码说明本方法只能针对实例使用。

  [System.Query]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ):本行代码说明扩展特性被使用了。

  扩展方法转换

  下表显示了在编译时进行的方法转换

  方法 代码编译为
1 expr . identifier ( ) identifier (expr)
2 expr . identifier ( args ) identifier (expr, args)
3 expr . identifier <typeargs> ( ) identifier <typeargs> (expr)
4 expr . identifier <typeargs> ( args ) identifier <typeargs> (expr, args)


  如果你在ILDASM中检查main方法的代码,它将会如下显示:

.method private hidebysig static void Main(string[] args) cil managed
{
 .entrypoint
 // Code size 42 (0x2a)
 .maxstack 1
 .locals init ([0] string s,
 [1] int32 i,
 [2] int32 j)
 IL_0000: nop
 IL_0001: ldstr "9"
 IL_0006: stloc.0
 IL_0007: ldloc.0
 IL_0008: call int32 ExtensionMethods.EMClass::ToInt32Ext(string)
 IL_000d: stloc.1
 IL_000e: ldloc.1
 IL_000f: call void [mscorlib]System.Console::WriteLine(int32)
 IL_0014: nop
 IL_0015: ldloc.0
 IL_0016: call int32 ExtensionMethods.EMClass::
 ToInt32Static(string)
 IL_001b: stloc.2
 IL_001c: ldloc.2
 IL_001d: call void [mscorlib]System.Console::WriteLine(int32)
 IL_0022: nop
 IL_0023: call string [mscorlib]System.Console::ReadLine()
 IL_0028: pop
 IL_0029: ret
} // end of method Program::Main
IL_0008: call int32 ExtensionMethods.EMClass::
ToInt32Ext(string)

  这里表明方法转换(expr . identifier ( ) <--> identifier (expr) )发生.

  所以当你调用 int i = s.ToInt32Ext();, 编译器内部进行操作int i = EMClass.ToInt32Ext(s);那么,重写的新生会当作一个静态方法调用来处理

  标识符按照如下的顺序解析:

  1. 最近的包含的命名空间声明

  2. 每个后继包含的命名空间声明

  3. 包含的编译单元

  下面是方法的从高到低的优先级:

  1. 实例方法

  2. 在同一个命名空间里的扩展方法

  3. 在当前命名空间之外的扩展方法

  为什么使用扩展方法?

  你也许会问:"为什么有了普通的静态和实例类还需要使用扩展方法呢?"其实,简单来说就是为了方便。我来举个例子吧。如果你在过去的一段时间内开发了很多函数形成了一个库。那么当某人要用这个函数库的时候,他必须要知道定义了所需的静态方法的类名。就像下面这个一样:

a = MyLibraryClass.


  这里,IntelliSense将会弹出并且告诉你可用函数的名字,你只需要挑选你所需要的。然后键入你需要的方法名和相关参数。

a = MyLibraryClass.DesiredFunction(strName)


  使用这种方法,你必须事先知道哪个库包含了你所要的哪个函数和它的名称,使用扩展方法就不一样了:

a = strName.


  这里,IntelliSense将会弹出,并且显示可以使用哪些扩展方法。你只需要键入你需要的扩展方法:

a = strName.DesiredFunction()


  这里无须给出所需的参数名来指定数据类型。

  在对象实例中调用静态方法

  扩展方法提供了一个新的机制用来在对象实例上调用静态方法。但和实例方法比较起来,它还是在功能上有诸多限制,因此你应该保守的使用它,主要将它哦能够在实例方法能力所不及的地方。

  C# 3.0并不是一个正式的版本,所以它的标准也没有最后定稿。因此,这样的格式也很有可能变化。

>>原作者:维维编译   >>来源:天极开发 

→ 相关文章

World of warcraft gold World of Warcraft gold gold world of warcraft gold world of warcraft Wow gold wow gold gold wow wow gold gold wow buy Cheap wow gold Buy Cheap WOW Goldff14 gils FF XIV Gils ffxiv gils Final Fantasy XIV Gils buying World of Warcraft Gold ffxiv gils Buy ffxiv gils ffxiv gils