c#

位置:IT落伍者 >> c# >> 浏览文章

C# 3.0中的分部方法


发布日期:2018年07月04日
 
C# 3.0中的分部方法

分部方法的语法

在看C#语言的Whats New时突然发现新特性列表的最后多出了一个Partial Method Definitions 但并不像其他新特性一样有超链接链接到其说明上网搜索了一下关于分部类型的信息非常少尤其是中文信息只有CSDN的 周融 在其《C# 语言在 Visual Studio Orcas Beta 上的新增功能(二) 》一文的最后提到了这个分部方法但没有进一步说明英文技术文章中倒是有两篇不错的heardaspx 和 partial methodswhatwhyandhowaspx

又仔细看了一下MSDN Library for Visual Studio Beta 终于对这个语言特性有所了解在这里介绍一下希望对大家有所帮助

分部方法的定义和分部类型类似只需在方法定义前添加partial关键字但分部方法只能拆分成两个部分——一部分是定义声明(Definition Declaration)另一部分是实现声明(Implement Declaration)其中定义声明看上去和抽象方法类似

partial class CA

{

// ……

private void partial M() // 定义声明|

而实现声明看上去和普通方法类似

private void partial M() // 实现声明

{

// 方法体

}

在调用分部方法时和调用其他方法一样

CA a = new CA()

aM()

只是如果只有定义声明而没有编写实现声明则编译器不会发射(Emit)该方法和调用该方法的语句的元数据与IL代码换言之如果没有编写实现声明则编译得到的程序集中CA类型里并没有M这个方法

使用分部方法的注意事项

分部方法的语法非常简单但有一些事项要注意

如果没有写实现声明则不会发射方法调用代码也不会对参数进行求值因此对于下面的例子

class CA  

  {  

      partial void M(int i);  

   

      static void Main()  

      {  

          CA a = new CA();  

          int i = ;  

          aM(i++);  

      }  

  }

分部方法M只有定义声明没有实现声明因此也不会发射调用该方法的代码aM(i++)因此也不会对i++进行求值所以最终i的值依然是但如果为M编写了实现声明则aM(i++)的代码会被编译到最终的程序集中同时参数也被求值i的值将被变为

分部方法只能出现在分部类中

分部方法必须是私有(private)的并且返回值类型必须是void

分部方法可以带有参数并且其参数可以带有thisparams和ref修饰符但不能带有out修饰符

分部方法不可以是虚拟(virtual)的

分部方法不可以是外部(extern)的

分部方法可以是静态(static)的也可以是不安全(unsafe)的

分部方法可以是泛型方法泛型约束必须放置在定义声明中但也可以在事先声明中重复说明在定义声明和实现声明中类型参数和类型参数的名字不一定必须一致

不能将分部方法封装到一个委托中

分部方法的应用场景

分部方法和分部类型的初衷是类似的一方面可以使得不同的开发者能够同时编写一个类型的不同部分另一方面可以分离自动生成的代码和用户手写的代码和分部类型一样分部方法也会在编译初期被合并成一个方法定义猜测从微软的角度来看第二个初衷可能才是真正的初衷

由此分部方法有如下几个应用场景(场景 出自In Case You Havent Heard这篇文章「caseyou haventheardaspx」)场景 出自Visual Studio 的Linq to SQL技术而场景 则是Anders Liu自已臆想出来的

场景 轻量级事件处理

有的时候自动生成的代码需要事件这类语言构造来通知用户对某些操作进行处理但实际上用于编写的代码就位于自动生成的类型之中此时或者需要触发一个事件或者就需要生成一个virtual方法来让用户继承但无论是事件还是继承开销都是比较大的所以可以通过分部方法来实现轻量级的处理方式如下面的类(本例子引用自前述的In Case You Havent Heard一文)

partial class Customer  

  {  

      string name;   

   

      public string Name  

      {  

          get  

          {  

              return name;  

          }  

          set  

          {  

              OnBeforeUpdateName();  

              OnUpdateName();  

              name = value;  

              OnAfterUpdateName();  

          }  

      }   

   

      partial void OnBeforeUpdateName();  

      partial void OnAfterUpdateName();  

      partial void OnUpdateName();  

  }  

这里定义了三个分部方法其意义不言而喻假设这是系统自动生成的代码则我们只需在另外一个源代码文件中的partial class Customer中实现这几个分部方法即可

场景 自定义DataContext中的InsertUpdateDelete方法

当使用Linq to SQL向项目中加入了实体类之后还会创建一个XxxDataContext类这个类继承自DataContext类并且是partial的这个类封装了具体的数据库操作功能(实体类仅封装数据库中的数据)如对象的插入更新和删除等

下面我们来看一下这个自动生成的类定义

[SystemDataLinqMappingDatabaseAttribute(Name=AdventureWorks)]  

  public partial class AdventureWorksDataContext : SystemDataLinqDataContext  

  {  

      

    private static SystemDataLinqMappingMappingSource mappingSource

= new AttributeMappingSource();  

      

    #region Extensibility Method Definitions  

    partial void OnCreated();  

    partial void InsertAWBuildVersion(AWBuildVersion instance);  

    partial void UpdateAWBuildVersion(AWBuildVersion instance);  

    partial void DeleteAWBuildVersion(AWBuildVersion instance);  

     

这里我们可以看到一系列的partial方法其中第一个OnCreated实际上属于场景中描述的情况是一个轻量级的事件表示 DataContext环境对象创建完毕而其他partial方法则用于自定义DataContext的IUD操作对于每一个表(实体类)这里都会出现一组InsertXxxUpdateXxx和DeleteXxx方法如果我们希望自定义删除行为(如希望将一个IsDelete字段设置为 true来表示已删除)则可以在另一个文件中扩展这个partial类并为对应的Delete方法提供实现声明

场景 新的调试信息输出方法

这是Anders Liu臆想的场景在分部方法的协助下我们可以写出这样的代码

partial class CA  

  {  

      partial void DebugPrint(string msg);  

    

      void F()  

      {  

            

          DebugPrint(aaa);  

      }  

  }   

   

  partial class CA  

  {  

  #if DEBUG  

      partial void DebugPrint(string msg);  

      {  

          DebugWriteLine(msg);  

      }  

  #endif  

  }   

这样做的好处在于我们还是反过来说罢如果不这样做必须在每次调用调试代码时都加入#if判断而这样可以将调试代码都写成方法在一处用#if进行判断

缺点在于由于分部方法必须是私有的所以必须针对每个类写一套调试代码

小结

总而言之Anders Liu在这篇文章里说的是分部方法

               

上一篇:C#中路径的几种获取方法及其区别

下一篇:设计模式与VB .net代码 外观模式,合成模式