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

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

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

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

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

posted @ 2010-06-14 17:42  李潘  阅读(388)  评论(0编辑  收藏  举报