C# 继承

简介

(封装、继承、多态)是面向对象编程三大特性之一,继承的思想就是摈弃代码的冗余,实现更好的重用性。继承从字面上理解,无外乎让人想到某人继承某人的某些东西,一个给一个拿。这个语义在生活中,就像家族继承财产,爷爷将财产继承给儿女,儿女在将财产继承给子孙,有些东西可以继承有些的东西只继承给某人。映射到编程当中,其思想也大致如此。

继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。 当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类。 继承的思想实现了 属于(IS-A) 关系。例如,哺乳动物 属于(IS-A) 动物,狗 属于(IS-A) 哺乳动物,因此狗 属于(IS-A) 动物。

基类和派生类

一个类可以实现多个接口,和继承一个类。(C#不允许多继承)

C# 中创建派生类的语法如下:

<访问修饰符符> class <基类>
{
 ...
}

<访问修饰符符> class <派生类> : <基类>
{
 ...
}

假设,有一个基类 Shape,它的派生类是 Rectangle:

namespace Malema.net
{
    public class Shape
    {
        public int Width { get; set; }
        public int Height { get; set; }

        ///重写了 object的ToString方法
        public override string ToString()
        {
            return $"Width: {Width}, Height:{Height}";
        }
    }

    // 派生类
    public class Rectangle : Shape
    {
        public int GetArea()
        {
            return (Width * Height);
        }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            Rectangle rect = new Rectangle();

            rect.Width = 5;
            rect.Height = 6;

            // 打印对象的面积
            Console.WriteLine("总面积: {0}", rect.GetArea()); //输出总面积30
            // 调用了 ToString方法
            Console.WriteLine(rect); // Width: 5, Height:6
            Console.ReadKey();
        }
    }
}

调用基类的构造函数 和重写方法

namespace Malema.net
{
    public class Shape
    {
        public Shape(int width, int height)
        {
            this.Width = width;
            this.Height = height;
        }

        public int Width { get; set; }
        public int Height { get; set; }

        public override string ToString()
        {
            return $"Width: {Width}, Height:{Height}";
        }

        //定义了一个可被重写的方法
        public virtual string Display()
        {
            return "I am Shape";
        }
    }

    // 派生类
    public class Rectangle : Shape
    {
        public string Name { get; set; }

        public Rectangle(int width, int height, string name) :
            base(width, height)
        {
            this.Name = name;
        }

        public int GetArea()
        {
            return (Width * Height);
        }

        // 重写了基类的方法
        public override string Display()
        {
            var temp = base.Display(); //复类里面也可以调用子类的方法。
            return "I am Rectangle";
        }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            Rectangle rect = new Rectangle(5, 6, "name1");

            // 打印对象的面积
            Console.WriteLine("总面积: {0}", rect.GetArea()); // 
            Console.WriteLine(rect); // Width: 5, Height:6
            Console.WriteLine(rect.Display()); // I am Rectangle
            Console.ReadKey();
        }
    }
}

实现多个接口

C# 不能继承于多个类,但是可以实现多个接口。意味着,这个类实现了多种接口约定。 如一只鸭子。 可以飞也可以游. 也可以吃东西。

如下:

namespace Malema.net
{
    public class Animal
    {
        public void Eat()
        {
            Console.WriteLine("我会吃");
        }
    }

    public interface IFly
    {
        void Fly();
    }

    public interface ISwim
    {
        void Swim();
    }

    public class Duck : Animal, IFly, ISwim
    {
        public void Fly()
        {
            Console.WriteLine("我开始飞了");
        }

        public void Swim()
        {
            Console.WriteLine("我开始游了");
        }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            var duck = new Duck();
            duck.Eat();
            LetUsEat(duck);
            LetUsFly(duck);// 这些不是直接调用duck.Fly(). 因为如果这样调的话,IFly接口的作用就体现不出来了
            LetUsSwim(duck);
        }

        public static void LetUsEat(Animal animal)
        {
            animal.Eat();
        }

        //有实现了IFly的我们都可以用这个方法来处理它了
        // 实际代码中这个方法正常也会被放到其它类当中
        public static void LetUsFly(IFly fly)
        {
            fly.Fly();
        }

        //有实现了ISwim的我们都可以用这个方法来处理它了。 
        public static void LetUsSwim(ISwim swim)
        {
            swim.Swim();
        }
    }
}

输出了

我会吃
我会吃
我开始飞了
我开始游了

为什么不弄一个大的接口含有两个方法呢。因为设计的时候偏向于越小越好。 因为并不是所有的动物都能飞的。(因为并不是所有的类都有办法满足一个大的接口).

组合优于继承

比如上面的代码。吃这个东西本质上是属于嘴巴的行为。 而嘴巴可以是鸭子的一部分。 其它动物如果需要的话也可以放入这个嘴巴。这样这个Eat方法实际就是由Month来决定了,而不是各个类自己来决定。 如下代码

namespace Malema.net
{
    //嘴巴
    public class Month
    {
        public void Eat()
        {
            Console.WriteLine("我是嘴巴我开始吃了");
        }
    }

    public class Duck 
    {
        public Month Month
        {
            get;
            set;
        }

        public void Eat()
        {
            Month.Eat();
        }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            var duck = new Duck();
            duck.Eat(); //我是嘴巴我开始吃了
        }
    }
}

继承的一个坏处继承层次多的时候。如果你想改动底层的一个方法的时候。你得考虑子类的依赖性。

下一篇:C# 多态
最近更新的
...