• More Effective C# Item7 : 不要为基类或者接口创建泛型的特殊实现


        引入泛型方法将让编译器对重载的解析变得非常复杂,每个泛型方法的类型参数都可以任意转换。如果稍有疏忽,程序的行为都将变得极其古怪,在创建泛型类或者方法时,必须保证让使用者能够尽可能的理解你的设计意图,安全的使用你的代码。

        我们来查看以下的代码,首先定义一个具有继承层次的结构。

    代码
    1 public class MyBase
    2 { }
    3
    4 public interface MyInterface
    5 {
    6 void WriteMessage();
    7 }
    8
    9 public class MyDerived : MyBase, MyInterface
    10 {
    11 public void WriteMessage()
    12 {
    13 Console.WriteLine("MyDerived.WriteMessage");
    14 }
    15 }
    16
    17 public class AnotherImpl : MyInterface
    18 {
    19 public void WriteMessage()
    20 {
    21 Console.WriteLine("AnotherImpl.WriteMessage");
    22 }
    23 }
       下面是测试代码。

    代码
    1 private static void WriteMessage(MyInterface obj)
    2 {
    3 Console.WriteLine("Inside WriteMessage(MyInterface)");
    4 obj.WriteMessage();
    5 }
    6
    7 private static void WriteMessage(MyBase obj)
    8 {
    9 Console.WriteLine("Inside WriteMessage(MyBase)");
    10 }
    11
    12 private static void WriteMessage<T>(T obj)
    13 {
    14 Console.WriteLine("Inside WriteMessge<T>()");
    15 Console.WriteLine(obj.ToString());
    16 }
    17
    18 private static void Test()
    19 {
    20 MyDerived derived = new MyDerived();
    21 WriteMessage(derived);
    22 Console.WriteLine();
    23 WriteMessage((MyInterface)derived);
    24 Console.WriteLine();
    25 WriteMessage((MyBase)derived);
    26 Console.WriteLine();
    27
    28 AnotherImpl another = new AnotherImpl();
    29 WriteMessage(another);
    30 Console.WriteLine();
    31 WriteMessage((MyInterface)another);
    32 }
        你能猜出程序的输出结果吗?

        程序的输出结果如下所示

        其实,下面这行代码是执行的最诡异的一行代码

    1 MyDerived derived = new MyDerived();
    2 WriteMessage(derived);
        这说明:对于一个派生于MyBase的对象来说,WriteMessage<T>(T obj)要比WriteMessage(MyBase b)在重载匹配上更加优先。这是因为通过将T替换成MyDerived,编译器即可完成一个精确的匹配,而WriteMessage(MyBase b)则还需要进行一次隐式类型转换,因此编译器在这种情况下选择了泛型版本的重载形式。

        而接下来的两个测试,则说明即使不在类型的继承体系中,编译器选择重载版本时,也会遵循同样的原则,即如果参数的运行类型不能够精确匹配非泛型版本中的参数类型时,那么编译器会优先考虑使用泛型版本的重载形式;如果参数的运行时类型能够精确匹配费泛型版本中的参数类型时,纳闷编译器会优先考虑使用非泛型版本的重载形式。相对于类型转换来说(这里指隐式类型转换),泛型版本更具优势。

        由此,我们可以看出编译器对名字解析的规则是非常有趣的,当你想支持某一个类型及其派生类型时,基于基类创建泛型并不是一个好的选择,同样,基于接口也是如此。我们在进行设计时,需要优先考虑让编译器自己决定参数的类型,而不是在程序中进行动态的判定。

        有时,我们也可以为特定的类型创建专门的处理方法,这时需要对重载类型的类型进行检查,如果符合条件,那么就会执行特定的逻辑。通常情况下,是不建议这么做的,因为这样违反了泛型的初衷,因为泛型就是对类型的抽象。

        在一个有继承关系或者接口实现的场景中,重载方法时需要特别注意,尤其是同时包含了非泛型版本的重载形式和泛型版本的重载形式,你需要非常清楚的知道,在运行时,到底是哪种形式的重载会被调用。

        
    作者:李潘
             
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    断开ssh链接在后台继续运行命令
    linux 隐藏显示终端光标
    shell脚本中echo显示内容带颜色
    Linux/Unix下pid文件作用浅析
    使用autotools自动生成Makefile并在此之上使用dh-make生成可发布的deb程序包(详解)
    Linux的tmpfs文件系统
    kernel编译
    Qt之读取配置文件
    android之TCP客户端框架
    android之模拟器更新底层
  • 原文地址:https://www.cnblogs.com/wing011203/p/1758315.html
Copyright © 2020-2023  润新知