C# 类和继承(扩展方法)
扩展方法
在迄今为止的内容中,你看到的每个方法都和声明它的类关联。扩展方法特性扩展了这个边
界,允许编写的方法和声明它的类之外的类关联。
想知道如何使用这个特性,请看下面的代码。它包含类MyData,该类存储3个double类型
的值,并含有一个构造函数和一个名为Sum的方法,该方法返回3个存储值的和。
在现实世界的开发中,扩展方法是一个特别有用的工具。事实上,几乎整个LINQ库都是通
过扩展方法来实现的。
class MyData
{private double D1; //字段private double D2;private double D3;public MyData(double d1,double d2,double d3) //构造韩{D1=d1;D2=d2;D3=d3;}public double Sum() //方法{return D1+D2+D3;}
}
这是一个非常有限的类,但假设它还含有另外一个方法会更有用,该方法返回3个数据的平
均值。使用已经了解的关于类的内容,有几种方法可以实现这个额外的功能。
- 如果你有源代码并可以修改这个类,你只需要为这个类增加这个新方法。
- 然而,如果不能修改这个类(如这个类在一个第三方类库中),那么只要它不是密封的,
你就能把它用作一个基类并在派生自它的类中实现这个额外的方法。
然而,如果不能访问代码,或该类是密封的,或有其他的设计原因使这些方法不适用,就不
得不在另一个使用该类的公有可用成员的类中编写一个方法。
例如,可以编写一个下面这样的类。下面的代码包含一个名为ExtendMyData的静态类,它
含有一个名为Average的静态方法,该方法实现了额外的功能。注意该方法接受MyData的实例作为参数。
static class ExtendMyData
{public static double Average(MyDat md) //MyData类的实例{return md.Sum()/3; .使用MyData的实例}
}class Program
{static void Main(){MyData md=new MyData(3,4,5);Console.WriteLine("Average:{0}",ExtendMyData.Average(md));//MyData的实例 调用静态方法}
}
运行结果
avergage=4
尽管这是非常好的解决方案,但如果能在类的实例自身上调用该方法,而不是创建另一个作用
于它的类的实例,将会更优雅。下面两行代码阐明了它们的区别。第一行使用刚展示的方法:在另
一个类的实例上调用静态方法。第二行展示了我们愿意使用的形式:在对象自身上调用实例方法。
ExtendMyData.Avergage(md) //静态调用形式
md.Average(); //实例调用形式
扩展方法允许你使用第二种形式,即使第一种形式可能是编写这种调用的正常方法。
通过对方法Average的声明做一个小小的改动,就可以使用实例调用形式。需要做的修改是
在参数声明中的类型名前增加关键字this,如下所示。把this关键字加到静态类的静态方法的
第一个参数上,把该方法从类ExtendMyData的常规方法改变为类MyData的扩展方法。现在两种
调用形式都可以使用。
扩展方法的重要要求如下。
- 声明扩展方法的类必须声明为static。
- 扩展方法本身必须声明为static。
- 扩展方法必须包含关键字this作为它的一个参数类型,并在后面跟着它扩展的类的
名称。
图8-22阐明了扩展方法的结构。
下面的代码展示了一个完整的程序,包括类MyData和声明在类ExtendMyData中的扩展方法
Average。注意方法Average完全如同它是MyData的实例成员那样调用!图8-22阐明了这段代码。
类MyData和ExtendMyData共同起到期望类的作用,带有3个方法。
namespace ExtensionMethods
{sealed class MyData{private double D1,D2,D3;public MyData(double d1,double d2,double d3){D1=d1;D2=d2;D3=d3;}public double Sum(){return D1+D2+D3;}}static class ExtendMyData {public static double Average(this MyData md) //关键字和类型{return md.Sum()/3;//声明为静态的}}class Program{static void Main(){MyData md=new MyData(3,4,5);Console.WriteLine($"Sum: {md.Sum()}");Console.WriteLine("Average:{0}",md.Average());}}
}
输出结果
Sum : 12
Average :4
命名约定
编写程序时会出现很多名称:类的名称、变量名称、方法名称、属性名称和许多其他名称。
阅读程序时,使用命名约定是为要处理的对象种类提供线索的重要方法。
第7章简单介绍过命名,现在你已经对类有了更多了解,我们可以给出更多细节了。表8-4
列出了3种主要的命名风格,以及它们在.NET程序中常见的使用方式。
可维护代码的一个重要支柱就是使用准确、自描述的变量名字(这个策略不太适合类似于
本书的图书、文章和示例代码,因为有其他的考虑)。对于变量名,不能过于追求简洁,否则
“欲速则不达"。
并不是所有人都认同这些命名约定,特别是前缀下划线。有些人认为前缀下划线非常有用,
有些人则认为它非常难看。微软本身对这个问题处理得也不够好。在推荐的命名约定中,微软没
有将前缀下划线作为一种选择,却在代码中大量使用本书将遵循微软官方推荐的命名约定,用Camel大小写作为私有和受保护的字段名称。
关于下划线还有一点要说明的是,它并不常出现在标识符的中间位置,不过事件处理程序除
外,这将在第15章介绍。