C# 继承和多态

C#继承与多态:理解virtual, override, new与abstract的关键区别

继承和多态

virtual必须重载吗? virtual可以直接调用吗? 多个修饰符之间顺序有要求吗? virtual必须完整定义吗? virtual后使用override和使用new隐藏有什么区别?

在派生类中使用new修饰符可以声明与基类中同名的方法和字段可以隐藏(覆盖)基类的方法和字段. 在基类的方法被隐藏的情况下, 调用派生类对象的方法究竟调用哪个类取决于对象声明的类型. 在c#中, 派生类其实是可以被声明成其基类, 可以跳过其被隐藏. 还用一种访问基类的被隐藏的方法的方法是在派生类中使用base关键字.

如果不希望在使用对象时还操心对象到底在声明时使用的什么类型, 就需要在基类中使用virtual关键字声明方法, 在派生类中使用override修饰方法. 但virtual修饰的对象可以直接通过基类的对象调用吗? 派生类可以不声明override的方法(必须重载吗)吗? 基类中没有使用virtual派生类中可以使用override吗? 基类中没有abstactvirtual修饰符不可以被override. vitural修饰的方法可以直接通过基类的对象直接调用. 派生类可以不重写基类的虚拟方法, 当调用派生类对象的虚拟方法时, 对用的将会是其父类的虚拟方法. 基类中没有虚拟修饰符的方法是不可以被重写的.

abstract用于修饰抽象类和抽象方法. 抽象类没有实例, 而且必须被继承. 抽象方法必须被重载, 重载为一个可以实现的方法或者一个新的抽象方法. 抽象方法定义时没有函数体, 但可以有参数吗? 可以直接使用new覆盖吗? 抽象类里可以声明和调用普通方法和虚拟方法吗? 抽象类中的方法如果不声明abstract是不能不写函数体的, 但这个方法如果不是静态的就不能直接通过类来调用. 换句话说, 通过类来调用的只能是静态成员. 抽象方法只能存在于抽象类中, 不可以在非抽象类中包含. 抽象方法定义时可以有参数, 但重写时需要保证参数类型和数量一致, 另外, 抽象方法不可以不注明返回类型. 基类的抽象方法不可以直接被new覆盖. 可以通过抽象类调用其普通方法, 只不过由于其不能被实例化, 需要将方法声明为静态的, 再通过抽象类访问. 由于需要将方法标记为静态, 并且静态方法不可修饰为虚拟, 所以不可以通过抽象类调用虚拟方法.

sealed用于修饰密封方法和密封类, 密封类不可被继承, 密封方法不可以被重载. 但要注意的是, 密封方法可以在其派生类中使用new给隐藏.

需要注意的是, 构造函数的继承稍显复杂, 如果基类中有声明的带参数的构造函数就需要在派生类中显式继承:

// 基类
public class BaseClass
{
    public BaseClass(type varName)
    {
        ...
    }
    ...
}
// 派生类
public class ChildClass : BaseClass
{
    public ChildClass(type varName) : base(varName)
    {
        ...
    }
    ...
}

c#使用如此多的关键字可能是为了尽可能消除语言的二意性.

设计实验:

  1. 一个方法, 被直接使用new隐藏. 该方法直接被派生类调用, 该方法的对象放在基类的数组中再被调用.
  2. 测试virtual的几个问题.
  3. 直接调用virtual方法.
  4. 尝试使用new隐藏.
  5. 测试abstract的几个问题.
  6. 编码机器抽象类和家用电器半抽象类. 机器抽象方法为启动和关闭. 家用电器继承自机器, 虚拟方法为待机. 设置家用电器数组, 存放空调, 电视实例.
  7. 使用6中的设计实现1-5.
public abstract class Machine
{
  public Machine(string name)
  {
      this.name = name.Trim();
  }
  protected enum Statuses : byte
  {
      off, on, standby
  }
  protected string name;
  public string Name
  {
      get { return name; }
      set { name = value; }
  }
  protected Statuses status = Statuses.off;
  public int StatusByte
  {
      get { return (byte)status; }
  }
  public string StatusString
  {
      get { return status.ToString(); }
  }
  public abstract void On();
  public abstract void Off();
  // public virtual static void Description()
  // 错误 CS0112 静态成员不能标记为“virtual”
  public static void Description()
  {
      Console.WriteLine("I am class Machine, I am an abstract class.");
  }
}

public class ElectricalApp : Machine
{
  public ElectricalApp(string name) : base(name){ }
  // public new void On()
  // 错误 CS0534 “ElectricalApp”不实现继承的抽象成员“Machine.On()”
  public override void On()
  {
      if (this.status == Statuses.on)
      {
          Console.WriteLine($"{this.name}: Already On.");
      }
      else
      {
          this.status = Statuses.on;
          Console.WriteLine($"{this.name}: Turn On!");
      }
  }
  public override void Off()
  {
      if (this.status == Statuses.off)
      {
          Console.WriteLine($"{this.name}: Already Off.");
      }
      else
      {
          this.status = Statuses.off;
          Console.WriteLine($"{this.name}: Turn Off!");
      }
  }
  // public abstract void Standby();
  // 错误 CS0513 “ElectricalApp.Standby()”是抽象的, 但它包含在非抽象类型“ElectricalApp”中
  public virtual void Standby()
  {
      if (this.status == Statuses.standby)
      {
          Console.WriteLine($"{this.name}: Already Standby.");
      }
      else
      {
          this.status = Statuses.standby;
          Console.WriteLine($"{this.name}: Turn Standby.");
      }
  }
  // public override void Test() { } // 在 Machine 中假设有public void Test(){}
  // 错误 CS0506 “ElectricalApp.Test()”: 继承成员“Machine.Test()”未标记为 virtual、abstract 或 override, 无法进行重写
}

public class Television : ElectricalApp
{
   public Television(string name) : base(name){ }
   public override void Standby() // 注意是使用 override 进行方法重写
   {
       if (this.status == Statuses.standby)
       {
           Console.WriteLine($"{this.name}:  Already Standby.");
       }
       else
       {
           this.status = Statuses.standby;
           Console.WriteLine($"Now you can't see anything on {this.name} without Off.");
       }
   }
}
public class AirConditioner : ElectricalApp
{
   public AirConditioner(string name) : base(name){ }
   public new void Standby() // 注意是使用 new 进行方法隐藏
   {
       if (this.status == Statuses.standby)
       {
           Console.WriteLine($"{this.name}:  Already Standby.");
       }
       else
       {
           this.status = Statuses.standby;
           Console.WriteLine($"There's not any air out from {this.name}, but it can be turned on quickly.");
       }
   }
}
internal class Program
{
   static void Main(string[] args)
   {
       // Machine machine = new Machine();
       // 错误 CS0144 无法创建抽象类型或接口“Machine”的实例
       // Machine.On(); // Machine 类中有非静态的public On 方法时
       // 错误 CS0120 对象引用对于非静态的字段、方法或属性“Machine.On()”是必需的

       Machine.Description();
       // 可以通过抽象类本身调用其静态方法.

       ElectricalApp myApp = new ElectricalApp("myApp");
       AirConditioner myAC = new AirConditioner("myAC");
       Television myTV = new Television("myTV");
       ElectricalApp[] myElectricalApps = { myApp, myAC, myTV };
       // ElectricalApp 数组, 其中的元素不一定是 ElectricalApp 类, 还有其派生类.

       Console.WriteLine("\nNow test Television!");
       myElectricalApps[2].Standby();
       // 由于使用的虚拟方法重写, 数组的 ElectricalApp 类声明不影响其派生类方法的调用.
       myTV.On();
       myTV.Standby();

       Console.WriteLine("\nNow test Air-conditioner!");
       myElectricalApps[1].Standby();
       // 使用 new 修饰符覆盖基类的方法, 当派生类被声明为基类时(这里体现在派生类存储与基类数组)
       // 对用的是其被隐藏的基类方法.
       myAC.On();
       myAC.Standby();
   }
}

上述实验输出为:

I am class Machine, I am an abstract class.

Now test Television!
Now you can't see anything on myTV without Off.
myTV: Turn On!
Now you can't see anything on myTV without Off.

Now test Air-conditioner!
myAC: Turn Standby.
myAC: Turn On!
There's not any air out from myAC, but it can be turned on quickly.
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值