zoukankan      html  css  js  c++  java
  • 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)则还需要进行一次隐式类型转换,因此编译器在这种情况下选择了泛型版本的重载形式。

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

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

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

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

        
    作者:李潘
             
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    PATA 1071 Speech Patterns.
    PATA 1027 Colors In Mars
    PATB 1038. 统计同成绩学生(20)
    1036. 跟奥巴马一起编程(15)
    PATA 1036. Boys vs Girls (25)
    PATA 1006. Sign In and Sign Out (25)
    读取web工程目录之外的图片并显示
    DOS命令
    java连接oracle集群
    servlet
  • 原文地址:https://www.cnblogs.com/wing011203/p/1758315.html
Copyright © 2011-2022 走看看