详解C#扩展方法原理及其使用

2020-06-24 11:03:21丽君

.class public auto ansi abstract sealed beforefieldinit TestExtension.StringExtension
 extends [System.Runtime]System.Object
{
 .custom instance void 
[System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute
::.ctor() = (
  01 00 00 00
 )
 // Methods
 .method public hidebysig static 
  int32 ToInt32 (
   string str
  ) cil managed 
 {
  .custom instance void 
[System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute
::.ctor() = (
   01 00 00 00
  )
  // Method begins at RVA 0x2050
  // Code size 31 (0x1f)
  .maxstack 2
  .locals init (
   [0] int32,
   [1] bool,
   [2] int32
  )

  IL_0000: nop
  IL_0001: ldarg.0
  IL_0002: ldloca.s 0
  IL_0004: call bool [System.Runtime]System.Int32::TryParse(string, int32&)
  IL_0009: stloc.1
  IL_000a: ldloc.1
  IL_000b: brfalse.s IL_0012

  IL_000d: nop
  IL_000e: ldloc.0
  IL_000f: stloc.2
  IL_0010: br.s IL_001d

  IL_0012: ldstr "无法转换为Int32类型"
  IL_0017: newobj instance void [System.Runtime]System.ArgumentException::.ctor(string)
  IL_001c: throw

  IL_001d: ldloc.2
  IL_001e: ret
 } // end of method StringExtension::ToInt32

} // end of class TestExtension.StringExtension

我们看一下调用后的效果,和直接调用静态方法一样TestExtension.StringExtension::ToInt32(string) ,至此,我们已经知道了扩展方法的使用了,编译器绑定,底层调用和静态调用一直,这也解释了一个问题,就是当类型为空的时候,为什么调用扩展方法了

.namespace Test
{
 .class private auto ansi beforefieldinit Test.Program
  extends [System.Runtime]System.Object
 {
  // Methods
  .method private hidebysig static 
   void Main (
    string[] args
   ) cil managed 
  {
   // Method begins at RVA 0x207b
   // Code size 24 (0x18)
   .maxstack 8
   .entrypoint

   IL_0000: nop
   IL_0001: ldstr "2"
   IL_0006: call int32 TestExtension.StringExtension::ToInt32(string)
   IL_000b: call void [System.Console]System.Console::WriteLine(int32)
   IL_0010: nop
   IL_0011: call string [System.Console]System.Console::ReadLine()
   IL_0016: pop
   IL_0017: ret
  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname 
   instance void .ctor () cil managed 
  {
   // Method begins at RVA 0x2094
   // Code size 8 (0x8)
   .maxstack 8

   IL_0000: ldarg.0
   IL_0001: call instance void [System.Runtime]System.Object::.ctor()
   IL_0006: nop
   IL_0007: ret
  } // end of method Program::.ctor

 } // end of class Test.Program

}

4、扩展方法的使用及其注意事项

扩展方法虽然很好用,但是如果我们扩展的对象发生了版本迭代,则会增加扩展方法失效的风险。

一下是在使用扩展方法时需要注意的地方

扩展方法与该类型中定义的方法具有相同的签名,编译器总是绑定到该实例方法,也就是扩展方法永远不会被调用,这也就回答了题目刚开始所说的问题。同时这个地方应该是考虑到了程序安全的问题,不然很容易出现代码注入问题。 当出现命名空间不同时,则需要使用using导入命名空间 同时扩展方法可以被修饰为internal,public,但需要类和扩展方法保持同样的修饰标识