• 《Effective C#》读书笔记——条目23:理解接口方法和虚方法的区别<使用C#表达设计>


      实现和覆写虚方法的区别:接口中声明的成员默认不是虚方法。派生类不能覆写基类中实现的接口成员。接口可以被显式实现,这会使针对该类的公有成员隐藏起来。接口与虚方法的概念不同,用法也不同。

    改变从基类继承的接口在派生类中的行为

      我们来看一个简单的例子:

     1         interface IMsg
     2         {
     3             void Message();
     4         }
     5         public class MyClass : IMsg
     6         {
     7             public void Message()
     8             {
     9                 Console.WriteLine("MyClass");
    10             }
    11         }
    12 
    13         public class MyDerivedClass : MyClass
    14         {
    15             public new void Message()
    16             {
    17                 Console.WriteLine("MyDerivedClass");
    18             }
    19         }
    20 
    21         public class EffectiveCSharp
    22         {
    23             public static void Main(string[] args)
    24             {
    25                 MyDerivedClass d = new MyDerivedClass();
    26                 d.Message();
    27                 IMsg m = d as IMsg;
    28                 m.Message();
    29 
    30                 Console.Read();
    31             }
    32         }

    运行输出:

      我们发现,将MyDerivedClass的实例做了转换之后,调用Message()方法变成了该类基类Class的Message()方法——有时候我们常常需要创建接口,然后在基类中实现它们,并且在派生类中更改它们的实现,这时候我们该怎么办呢?这时候有两种办法可供选择。

    1.将实现接口的基类中实现的接口成员定义成:virtual,并在派生类中override

     1         interface IMsg
     2         {
     3             void Message();
     4         }
     5         public class MyClass : IMsg
     6         {
     7             public virtual void Message()
     8             {
     9                 Console.WriteLine("MyClass");
    10             }
    11         }
    12 
    13         public class MyDerivedClass : MyClass
    14         {
    15             public override void Message()
    16             {
    17                 Console.WriteLine("MyDerivedClass");
    18             }
    19         }
    20 
    21         public class EffectiveCSharp
    22         {
    23             public static void Main(string[] args)
    24             {
    25                 MyDerivedClass d = new MyDerivedClass();
    26                 d.Message();
    27                 IMsg m = d as IMsg;
    28                 m.Message();
    29 
    30                 Console.Read();
    31             }
    32         }

    运行输出:

    2.将实现接口的基类定义成抽象类,并将实现的接口成员定义为抽象成员

      我们同时可以将派生类的重写方法定义成密封的防止其派生类再重写该方法:

     1         interface IMsg
     2         {
     3             void Message();
     4         }
     5         public abstract class MyClass : IMsg
     6         {
     7             public abstract void Message();
     8         }
     9 
    10         public class MyDerivedClass : MyClass
    11         {
    12             public sealed override void Message()
    13             {
    14                 Console.WriteLine("MyDerivedClass");
    15             }
    16         }
    17 
    18         public class EffectiveCSharp
    19         {
    20             public static void Main(string[] args)
    21             {
    22                 MyDerivedClass d = new MyDerivedClass();
    23                 d.Message();
    24                 IMsg m = d as IMsg;
    25                 m.Message();
    26                 MyClass c = (MyClass)m;
    27                 c.Message();
    28                 Console.Read();
    29             }
    30         }

     运行输出:

    派生类继承基类中接口的实现

      其实派生类可以从基类中继承基类对接口的实现,因为派生类可以把该接口的声明成为其契约的一部分,即使它并没有实现任何该接口中成员的实现,只要类的某个公开可访问的方法与接口的签名相匹配,那么契约的条件即可满足,不过这种方法无法使用显示接口实现。例如下面的示例:

     1        interface IMsg
     2         {
     3             void Message();
     4         }
     5         public abstract class MyClass : IMsg
     6         {
     7             public virtual void Message()
     8             {
     9                 Console.WriteLine("MyClass");
    10             }
    11         }
    12 
    13         public class MyDerivedClass : MyClass,IMsg
    14         {
    15         }
    16 
    17         public class EffectiveCSharp
    18         {
    19             public static void Main(string[] args)
    20             {
    21                 MyDerivedClass d = new MyDerivedClass();
    22                 d.Message();
    23                 IMsg m = d as IMsg;
    24                 m.Message();
    25                 MyClass c = (MyClass)m;
    26                 c.Message();
    27                 Console.Read();
    28             }
    29         }

     运行输出:

    小节

    实现接口拥有的选择要比创建和覆写虚函数多。我们可以为类层次创建密封类(sealed)的实现、虚实现或者抽象实现。我们还可以创建密封的实现,并在实现接口的方法中提供虚方法调用。我们也可以决定派生类应该如何及何时修改基类中实现的接口成员的默认行为。接口不是虚方法,而是一个单独的契约。

  • 相关阅读:
    c++趣味之难以发现的bug
    解决html5 canvas 绘制字体、图片与图形模糊问题
    c++趣味之shared_ptr额外好处
    标准mysql(x64) Windows版安装过程
    解决Chrome与jQuery菜单兼容问题
    在Linux与Windows上获取当前堆栈信息
    TypeScript技巧集锦(陆续更新)
    c++趣味之变量名,颠覆所有教科书的VisualStudio
    Web前端:博客美化:三、右上角的Github Ribbon
    Web前端:博客美化:二、鼠标特效
  • 原文地址:https://www.cnblogs.com/IPrograming/p/EffectiveCSharp_23.html
Copyright © 2020-2023  润新知