继承

整理要点

前言

  面向对象(Object-Oriented, OO):

    C#不是一种纯粹的面向对象编程语言。

    C#提供了多种编程范例。

    然而,面向的对象是C#的一种重要概念,也是.NET提供的所有库的核心原则。

    面向对象的三大重要概念:封装、继承和多态性。

继承的类型

  • 单重继承:表示一个类可以派生自一个基类。C#就采用这种继承。  
  • 多重继承多重继承允许一个类派生自多个类。C#不支持类的多重继承,但允许接口的多重继承。
  • 多层继承:允许继承有更大的层次结构。例如:类B继承于A,类C又继承于B。其中,类B也称为中间基类。C#支持多层继承,且很常用。
  • 接口继承:定义了接口的继承。这里允许多重继承。

  多重继承

    一些语言(如C++)支持所谓的“多重继承”,即一个类可以继承多个类。对于实现继承,多重继承会给生成的代码增加复杂性,还会带来一些开销。因此,C#不支持多重继承。

    而C#允许类型派生自多个接口。一个类可以实现多个接口。即:一个类可以派生自另一个类和任意多个接口。

    准确地说,System.Object是一个公共的基类,所以每个C#类(除了Object类之外)都有一个基类,还可以有任意多个基类接口。

  结构和类:

    结构不支持继承,但每个结构都自动派生自System.ValueType。

    不能编码实现结构的类型层次,但结构可以实现接口。即:结构并不支持实现继承,但支持接口继承。

实现继承

  一个类可以派生自另一个类和任意多个接口:

public class Animal
{
    public string Name { get; set; }
}
public interface IAction
{
    void Eat();
}
public class Dog: Animal, IAnimal
{
    public void Eat()
    {
         //
    }
}

  虚方法:

    把一个基类方法声明为virtual,就可以在任何派生类中重写该方法

    在派生类中重写虚方法时使用orverride关键字显示声明:

/// <summary>
/// 动物类
/// </summary>
public class Animal
{
    public virtual void Eat()
    {
        Console.WriteLine("吃东西!");
    }
}
/// <summary>
/// 狗类
/// </summary>
public class Dog : Animal
{
    public override void Eat()
    {
        Console.WriteLine("狗吃骨头!");
    }
}
/// <summary>
/// 猫类
/// </summary>
public class Cat : Animal
{
    public override void Eat()
    {
        Console.WriteLine("猫吃鱼!");
    }
}

  多态性:

  例如:某个方法的参数使用基类类型定义,任何该基类的派生类都可以作为参数使用该方法。

  代码如下:

 1 /// <summary>
 2 /// 表示动物吃什么的方法
 3 /// </summary>
 4 /// <param name="animal">使用基类(动物类)定义</param>
 5 public void EatWhat(Animal animal)
 6 {
 7     animal.Eat();
 8 }
 9 
10 // 调用时,就可以使用具体对象
11 var dog = new Dog(); // 创建一个狗对象
12 var cat = new Cat(); // 创建一个猫对象
13 EatWhat(dog); // 狗吃骨头!
14 EatWhat(cat); // 猫吃鱼!

  隐藏方法:

    如果基类和派生类中拥有签名相同的方法,但该方法没有分别声明为virtual和override,那么派生类方法就会隐藏基类方法。

  例如:

 1 /// <summary>
 2 /// 动物类
 3 /// </summary>
 4 public class Animal
 5 {
 6     /// <summary>
 7     /// 基类中没有声明为虚方法
 8     /// </summary>
 9     public void Eat()
10     {
11         Console.WriteLine("吃东西!");
12     }
13 }
14 /// <summary>
15 /// 狗类
16 /// </summary>
17 public class Dog : Animal
18 {
19     /// <summary>
20     /// 派生类中也没有使用override关键字重写
21     /// 使用new关键字表示覆盖基类的方法
22     /// </summary>
23     public new void Eat()
24     {
25         Console.WriteLine("狗吃骨头!");
26     }
27 }
28 /// <summary>
29 /// 猫类
30 /// </summary>
31 public class Cat : Animal
32 {
33     /// <summary>
34     /// 派生类中也没有使用override关键字重写
35     /// 使用new关键字表示覆盖基类的方法
36     /// </summary>
37     public new void Eat()
38     {
39         Console.WriteLine("猫吃鱼!");
40     }
41 }

    new 方法修饰符不应该故意用于隐藏基类的方法,主要是来解决版本冲突,在修改派生类后,响应基类的变化。

    最好使用在基类中指定方法为虚方法,在需要的时候,在派生类中使用override关键字重写基类方法。

  调用方法的基类版本:

 1 /// <summary>
 2 /// 狗类
 3 /// </summary>
 4 public class Dog : Animal
 5 {
 6     /// <summary>
 7     /// 派生类中也没有使用override关键字重写
 8     /// 使用new关键字表示覆盖基类的方法
 9     /// </summary>
10     public new void Eat()
11     {
12         base.Eat();
13         Console.WriteLine("狗吃骨头!");
14     }
15 }

  抽象类和抽象方法:

  C#允许把类和方法声明为abstract。

  • 抽象类不能被实例化
  • 抽象方法不能直接显示,必须在非抽象的派生类中重写。
  • 所以抽象方法本身就是虚拟的,不需要也不能再显示的声明为virtual
  • 如果类中包含抽象方法,则该类也是抽象的,也必须显示的声明为抽象类!

  例如:

 1 /// <summary>
 2 /// 动物类
 3 /// </summary>
 4 public abstract class Animal
 5 {
 6     /// <summary>
 7     /// 抽象方法不能直接实现!
 8     /// </summary>
 9     public abstract void Eat();
10 }
11 /// <summary>
12 /// 狗类
13 /// </summary>
14 public class Dog : Animal
15 {
16     /// <summary>
17     /// 抽象方法必须在非抽象的派生类中使用override关键字重写
18     /// </summary>
19     public override void Eat()
20     {
21         Console.WriteLine("狗吃骨头!");
22     }
23 }
24 /// <summary>
25 /// 猫类
26 /// </summary>
27 public class Cat : Animal
28 {
29     /// <summary>
30     /// 抽象方法必须在非抽象的派生类中使用override关键字重写
31     /// </summary>
32     public override void Eat()
33     {
34         Console.WriteLine("猫吃鱼!");
35     }
36 }
37 
38 // 抽象方法不能直接实例化,可以使用基类声明和创建派生类的实例
39 Animal dog = new Dog();
40 Animal cat = new Cat();

  密封类和密封方法:

  表示指定的类不能被继承或指定的方法不能被重写。

  使用关键字sealed:

  

 1 public sealed class Animal
 2 {
 3     /// <summary>
 4     /// 抽象方法不能直接实现!
 5     /// </summary>
 6     public void Eat()
 7     {
 8         Console.WriteLine("吃东西!");
 9     }
10 }
11 public class Dog : Animal // 报错,Animal是密封类 无法被继承
12 {
13 
14 }

  密封方法不能被重写:

 1 public abstract class Animal
 2 {
 3     public abstract void Eat();
 4 }
 5 public class Dog : Animal
 6 {
 7     /// <summary>
 8     /// 表示密封方法
 9     /// </summary>
10     public sealed override void Eat()
11     {
12        Console.WriteLine("吃骨头!");
13     }
14 }
15 
16 /// <summary>
17 /// 表示柯基狗
18 /// </summary>
19 public class KeJiDog : Dog
20 {
21     /// <summary>
22     /// 派生类中无法重写!
23     /// </summary>
24     public override void Eat() // 报错
25     {
26         Console.WriteLine("柯基狗吃骨头!");
27     }
28 }

  派生类的构造函数:

  如果一个基类中没有声明无参数的构造函数,那么当派生类继承它是也必须要实现基类中的构造函数。

  如果基类中有多个有参的构造函数,可以在派生类中自行指定要实现的基类的构造函数(根据参数的不同)

  例如:

 1 public class Animal
 2 {
 3     public Animal(string food)
 4     {
 5         Console.WriteLine($"吃:{food}");
 6     }
 7 
 8     public Animal(string food, string food2)
 9     {
10         Console.WriteLine($"吃:{food}和 {food2}");
11     }
12 }
13 public class Dog : Animal
14 {
15     public Dog() : base("骨头")
16     {
17     }
18 }

访问修饰符

修饰符 应用于 说明
 public  所有类型或成员  任何代码均可以访问该项
 protected  类型和内嵌类型的所有成员  只有派生类的类型能访问该项
 internal  所有类型或成员  只能在包含它的程序集中访问该项
 private  类型和内嵌类型的所有成员  只能在它所属的类型中访问该项(类自身的内部)
 protected internal  类型和内嵌类型的所有成员  只能在包含它的程序集和派生类型的任何代码中访问该项
 private protected  类型和内嵌类型的所有成员  C# 7.2新增的修饰符,只允许访问同一程序集中的派生类型,而不允许访问其他程序集中的派生类型
public、protected、private是逻辑访问修饰符。internal是一个物理访问修饰符,其边界是一个程序集。

接口

  接口是约束指定派生类的一组行为。

  • 接口成员不能有任何实现
  • 接口只能包含方法、属性、索引器和事件的声明
  • 接口不能实例化,只能包含成员的签名
  • 接口总是抽象的,所以接口不需要abstract关键字
  • 接口既不能有构造函数,也不能有字段
  • 接口成员不能带有修饰符
  • 接口成员总是隐式为public,不能声明为virtual

  定义和实现接口:

  接口的命名通常以字母I开头,以便知道这是一个接口

 1 public interface IAction
 2 {
 3     void Run();
 4 }
 5 
 6 public class Action : IAction
 7 {
 8     public void Run()
 9     {
10 
11     }
12 }

  声明接口类型的变量

1 IAction action = new Action();

  派生的接口:

  

 1 public interface IBehavior
 2 {
 3     void Cry();
 4 }
 5 
 6 public interface IAction : IBehavior
 7 {
 8     void Run();
 9 }
10 
11 public class Action : IAction
12 {
13     public void Run()
14     {
15         throw new NotImplementedException();
16     }
17 
18     public void Cry()
19     {
20         throw new NotImplementedException();
21     }
22 }

is和as运算符

  as:

1 public void HandleAction(Action action)
2 {
3 
4 }
5 
6 IAction action = new Action();
7 HandleAction(action as Action);

  is:

 1 public void HandleAction(object o)
 2 {
 3     if (o is IAction action)
 4     {
 5 
 6     }
 7 }
 8 
 9 IAction action = new Action();
10 HandleAction(action);
posted @ 2020-04-19 12:17  VTing4You  阅读(171)  评论(0编辑  收藏  举报