zoukankan      html  css  js  c++  java
  • 第4章 继承

    1. 继承的类型

    • C#类可以派生自另一个类和任意多个接口。
    • 结构总派生自System.ValueType,故不支持实现继承,但支持接口继承,即可以派生自任意多个接口。
    • 类总派生自System.Object或用户选择的另一个类,还可以派生自任意多个接口。
    • 如果类和接口都用于派生,则类总是必须放在接口的前面。

    2.实现继承

    (1) 虚方法:

    • 基类方法或属性声明命名为virtual,任何派生类中使用override显示声明来重写该方法或属性。
    • 成员字段和静态函数不能声明为virtual,因为虚方法只对类中的实例函数成员有意义。

    (2) 多态性:

    • 方法中接收基类作为参数,则任何派生自基类的类型都可以传递给该方法。
    • 方法中使用参数的方法,如不是虚拟参数或没有重写派生类的方法,则执行基类方法;否则执行派生类方法。

    (3) 隐藏方法:(尽量不使用)

    • 若签名相同的方法,在基类和派生类中都进行了声明,但该方法没有分别声明为virtual和override,派生类方法就会隐藏基类方法。
    • 使用new关键字隐藏方法,避免出现编译警告,用于处理版本冲突。

    (4) 调用方法的基类版本:使用base关键字,在派生类中调用基类方法。

    (5) 抽象类和抽象方法:

    • 类和方法可以使用abstract关键字声明为抽象类和抽象方法。
    • 类中包括抽象方法,则该类也必须声明为抽象类。
    • 抽象类不能实例化;抽象方法不能直接实现,必须在非抽象的派生类中重写(关键字override);派生类中需要实现所有抽象成员。
    • 虽然抽象类不能实例化,但可以声明一个抽象类变量,由实例化的派生类将其分配给基类。
    // 抽象基类 Shape
    public abstract class Shape
    {
          public abstract void Resize(int width, int height); //抽象方法
    }
     
    // 派生类 Ellipse
    public class Ellipse : Shape
    {
          public override void Resize(int width, int height)
          {
                Size.Width = width;
                Size.Height = height;
          }
    }
     
    //实例化
    Shape s1 = new Ellipse();

     (6) 密封类和密封方法:sealed修饰符

    • 密封类表示不允许创建该类的子类。
    • 密封方法表示不能重写该方法。
    • 常用密封方法来终止继承:方法可以是基类的重写方法,但其继承类中不能扩展该方法。
    class MyClass: MyBaseClass
    {
         public sealed override void FinalMethod()
         {
              // implementation
         }
    }
    
    class DerivedClass: MyClass
    {
         public override void FinalMethod()   //wrong!Will give compilation error
         {
         }
    }

     (7) 派生类的构造函数:

    • 对于派生类,需保证通过层次结构进行构造。
    • 构造函数总是按照层次结构的顺序调用:先调用System.Object类的构造函数 -> 再按照层次结构由上向下进行 -> 直到到达编译器要实例化的类为止。
    // 使用自动属性初始化器初始化属性
    // 编译器会创建一个默认的构造函数
    public class Sharp
    {
            public Position Position { get; } = new Position();
            public Size Size { get; } = new Size();
    }
    
    // 派生类Ellipse
    public class Ellipse : Sharp
    {
           public Ellipse()
                  : base()
           {
           }
    }
    
    // 实例化Ellipse类,构造函数调用顺序为
    // Object 构造函数 -> Sharp 构造函数 -> Ellipse 构造函数
    •  使用构造函数初始化器调用带参数的基类构造函数的赋值方式
    // 基类构造函数内进行赋值,非默认初始化
    public class Shape
    {
            public Shape(int width, int height, int x, int y)
            {
                Size = new Size { Width = width, Height = height };
                Position = new Position { X = x, Y = y };
            }
    }
    
    //  派生类
    public class Ellipse : Shape
    {
            // 派生类中创建构造函数,用构造函数初始化器初始化基类的构造函数
            public Ellipse(int width, int height, int x, int y)
                : base(width, height, x, y)
            {
    
            }
            // 希望允许使用默认的构造函数创建Ellipse对象
            public Ellipse()
                : base( 0, height: 0, x: 0, y: 0)
            {
    
            }
    }

     3. 接口:interface

    • 接口只包括方法、属性、索引器和事件的声明。
    • 不允许提供接口中任何成员的实现方式。
    • 比较接口和抽象类:

    (1) 定义和实现接口

    • 接口的命名采用Hungarian表示法,即在名称前加一个字母,表示所定义对象的类型。接口类型字母为I。
        public interface IBankAccount
        {
            void PayIn(decimal amount);
            bool Withdraw(decimal amount);
            decimal Balance { get; }
        }
        // 派生类 SaverAccount
        public class SaverAccount : IBankAccount
        {
            private decimal _balance;
    
            public void PayIn(decimal amount) => _balance += amount;
    
            public bool Withdraw(decimal amount)
            {
                if (_balance >= amount)
                {
                    _balance -= amount;
                    return true;
                }
                WriteLine("Withdrawal attempt failed.");
                return false;
            }
    
            public decimal Balance => _balance;
    
            public override string ToString() =>
                $"Venus Bank Saver: Balance = {_balance,6:C}";
        }
    
    
        // 派生类  GoldAccount
        public class GoldAccount : IBankAccount
        {
             //etc
        }
    • 接口仅表示其成员的存在性,派生的类负责确定这些成员是虚拟还是抽象的(但只有在类本身是抽象的,这些函数才能是抽象的)。
    • 接口引用安全可以按成类引用——但接口引用强大之处在于,它可以引用任何实现该接口的类。
    IBankAccount[] accounts = new IBankAccount[2];
    account[0] = new SaverAccount();
    account[1] = new GoldAccount();

     (2) 派生的接口

    • 接口可以彼此派生,实现派生接口的类必须实现相关接口的所有方法。
        public interface ITransferBankAccount : IBankAccount
        {
            bool TransferTo(IBankAccount destination, decimal amount);
        }
       public class CurrentAccount : ITransferBankAccount
        {
            private decimal _balance;
    
            public void PayIn(decimal amount) => _balance += amount;
    
            public bool Withdraw(decimal amount)
            {
                if (_balance >= amount)
                {
                    _balance -= amount;
                    return true;
                }
                WriteLine("Withdrawal attempt failed.");
                return false;
            }
    
            public decimal Balance => _balance;
    
            public bool TransferTo(IBankAccount destination, decimal amount)
            {
                bool result = Withdraw(amount);
                if (result)
                {
                    destination.PayIn(amount);
                }
                return result;
            }
    
            public override string ToString() =>
              $"Jupiter Bank Current Account: Balance = {_balance,6:C}";
        }
    • 在实现并调用派生接口的方法时,不必知道该对象类型,只需要知道该对象实现了接口即可。

    4. is 和 as 运算符: 与继承有关

    • as运算符:返回对象的引用,且不抛出InvalidCastException异常;如不是所需要类型,返回null。
    public void WorkWithManyDifferentObject(object o)
    {
        IBankAccount account = o as IBankAccount;
        if (account != null)
        {
              //work with the account
        }
    }
    •  is运算符:根据对象是否使用指定的类型,返回true或false。
    public void WorkWithManyDifferentObject(object o)
    {
        if (o is IBankAccount)
        {
              IBankAccount account = (IBankAccount)o;
              //work with the account
        }
    }

    注:文中代码均来自《C#高级编程(第10版)C# 6 & .NET Core 1.0》

  • 相关阅读:
    Tips
    react
    Vue 双向绑定
    jQuery 学习笔记
    CC NOV17
    一种高效处理无修改区间或树上询问的数据结构(附代码)
    HNOI 2017
    PA2015
    bzoj 泛做
    GG
  • 原文地址:https://www.cnblogs.com/zhangjbravo/p/9430434.html
Copyright © 2011-2022 走看看