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的实例作为参数。

csharp 复制代码
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

尽管这是非常好的解决方案,但如果能在类的实例自身上调用该方法,而不是创建另一个作用

于它的类的实例,将会更优雅。下面两行代码阐明了它们的区别。第一行使用刚展示的方法:在另

一个类的实例上调用静态方法。第二行展示了我们愿意使用的形式:在对象自身上调用实例方法。

csharp 复制代码
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());
        }
    }
}

输出结果

csharp 复制代码
Sum : 12
Average :4

命名约定

编写程序时会出现很多名称:类的名称、变量名称、方法名称、属性名称和许多其他名称。

阅读程序时,使用命名约定是为要处理的对象种类提供线索的重要方法。

第7章简单介绍过命名,现在你已经对类有了更多了解,我们可以给出更多细节了。表8-4

列出了3种主要的命名风格,以及它们在.NET程序中常见的使用方式。

可维护代码的一个重要支柱就是使用准确、自描述的变量名字(这个策略不太适合类似于

本书的图书、文章和示例代码,因为有其他的考虑)。对于变量名,不能过于追求简洁,否则

"欲速则不达"。

并不是所有人都认同这些命名约定,特别是前缀下划线。有些人认为前缀下划线非常有用,

有些人则认为它非常难看。微软本身对这个问题处理得也不够好。在推荐的命名约定中,微软没

有将前缀下划线作为一种选择,却在代码中大量使用本书将遵循微软官方推荐的命名约定,用Camel大小写作为私有和受保护的字段名称。

关于下划线还有一点要说明的是,它并不常出现在标识符的中间位置,不过事件处理程序除

外,这将在第15章介绍。

相关推荐
大春儿的试验田14 分钟前
Parameter ‘XXX‘ not found. Available parameters are [list, param1]
java
不爱写代码的玉子19 分钟前
HALCON透视矩阵
人工智能·深度学习·线性代数·算法·计算机视觉·矩阵·c#
程序员JerrySUN1 小时前
[特殊字符] 深入理解 Linux 内核进程管理:架构、核心函数与调度机制
java·linux·架构
2302_809798321 小时前
【JavaWeb】Docker项目部署
java·运维·后端·青少年编程·docker·容器
网安INF1 小时前
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
java·web安全·网络安全·flink·漏洞
一叶知秋哈1 小时前
Java应用Flink CDC监听MySQL数据变动内容输出到控制台
java·mysql·flink
jackson凌1 小时前
【Java学习笔记】SringBuffer类(重点)
java·笔记·学习
sclibingqing2 小时前
SpringBoot项目接口集中测试方法及实现
java·spring boot·后端
程序员JerrySUN2 小时前
全面理解 Linux 内核性能问题:分类、实战与调优策略
java·linux·运维·服务器·单片机
糯米导航2 小时前
Java毕业设计:办公自动化系统的设计与实现
java·开发语言·课程设计