代码改变世界

Effective C# 学习笔记(三十四)避免在子类中重载父类的方法

2011-07-18 12:48  小郝(Kaibo Hao)  阅读(546)  评论(0编辑  收藏  举报

首先,要分清orverload和override两个单词的中文解释。

overload为重载即表示方法通过参数个数、参数类型的不同对同名方法的另一逻辑实现,等于创建了一个新的同名方法,与原有方法并存。

override为覆写,顾名思义就是覆盖掉父类同名方法,形参列表是与父类一致的。

下面举例说明overload给程序带来的复杂问题:

//一个父类

using System;

using System.Collections.Generic;

namespace C_Sharp_Learning.Overload

{

    public class B

    {

        public void Foo(D2 parm)

        {

            Console.WriteLine("In B.Foo");

        }

        public void Bar(B2 parm)

        {

            Console.WriteLine("In B.Bar");

        }

        public void Foo2(IEnumerable<D2> parm)

        {

            Console.WriteLine("In B.Foo2");

        }

    }

}

//一个子类

using System;

using System.Collections.Generic;

namespace C_Sharp_Learning.Overload

{

    public class D : B

    {

        public void Foo(B2 parm)

        {

            Console.WriteLine("In D.Foo");

        }

        public void Bar(B2 parm1, B2 parm2 = null)

        {

            Console.WriteLine("In D.Bar");

        }

        public void Foo2(IEnumerable<B2> parm)

        {

            Console.WriteLine("In D.Foo2");

        }

    }

}

//一个参数父类

namespace C_Sharp_Learning.Overload

{

    public class B2

    {

    }

}

//一个参数子类

namespace C_Sharp_Learning.Overload

{

    public class D2 : B2

    {

    }

}

 Console.WriteLine("-----------overload-----------");

 

 var obj2 = new D();

 Console.WriteLine(obj1);//C_Sharp_Learning.Overload.D

 obj2.Foo(new D2());//In D.Foo  即使父类的方法形参列表更复合参数类型,但编译器依然认为子类的重载优先级更高,所以调用了子类的方法

 obj2.Foo(new B2());//In D.Foo

 C_Sharp_Learning.Overload.B obj3 = new D();

 Console.WriteLine(obj3);//C_Sharp_Learning.Overload.D 运行时解析为子类

 obj3.Foo(new D2());//In B.Foo(由于在编译时obj3定义为B类型,所以obj3.Foo方法调用的是父类的Foo)

 var obj4 = new D();

 (obj4 as C_Sharp_Learning.Overload.B).Foo(new D2());//In B.Foo 可用类型转换来控制调用父类的方法

 obj4.Foo(new B2());//In D.Foo

 obj1 = new D();

 obj1.Bar(new D2());//In D.Bar 利用C#4.0 可选参数特性进行的重载,依然使用了子类的方法

 (obj1 as C_Sharp_Learning.Overload.B).Bar(new D2());//In B.Bar 依然利用类型临时转换调用父类的方法

 var sequence = new List<D2> { new D2(), new D2() };

 obj2 = new D();

 obj2.Foo2(sequence);

/*

这里的情况比较复杂,在c#4.0以前的版本,应该是调用父类的方法,因为那时C#的范型不支持协变和逆变,所以输出为In B.Foo2

而在C#4.0中,引入了范型类型的协变逆变特性,所以输出为In D.Foo2

*/

如上代码所展现的,子类对于父类方法的重载是不易被理解的,也是很容易引发类的使用者的使用错误。子类的重载的优先级默认是高于父类的方法的,即使父类的方法的参数类型更符合调用参数的类型也是如此。另外注意C#4.0版本前后对范型的校验规则因协变逆变的支持不同,会产生更多运行时版本兼容性的问题,所以尽量避免在子类中重载父类的方法。