首页 > 开发 > 综合 > 正文

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

2024-07-21 02:29:26
字体:
来源:转载
供稿:网友
  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并不是一个正式的版本,所以它的标准也没有最后定稿。因此,这样的格式也很有可能变化。


收集最实用的网页特效代码!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表