C# 继承和多态

继承和多态

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.
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值