从设计基类及其派生类看继承关系

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从设计基类及其派生类看继承关系相关的知识,希望对你有一定的参考价值。

继承能够定义可重用、扩展或修改父类行为的子类。但基类的静态构造函数、实例构造函数和析构函数不能被派生类继承。

 在下面实例中,定义一个基类Publication用于表示任何类型的出版物以及派生至Publication的其他类型Book类,由此也可以扩展为定义其他类型如:Magazine、Journal、Newspaper和Article。

在设计基类Publication时我们必须考虑到如下关系:

1.要在基类中添加哪些成员

2.基类是否用作派生类模板的抽象基类

3.类层次结构的扩展空间大小,要开发包含三个或多个的类的层次结构,如Publication是Periodical的基类,又可以是Magazine和Journal的基类

4.能否重写基类实现的代码,如果容许重写,必须在基类中使用关键字virtual,派生类才能允许重写基类方法。

5.派生类是否必须继承结构的终结类,且本身不被用作其他派生类的基类,这时可以用sealed 关键字来标识该类

先定义一个出版物类型的枚举

 public enum PublicationType:long
    {
        [Description("报纸")]
        Newspaper=1,
        [Description("杂志")]
        Magazine =2,
        [Description("书籍")]
        Book=3
    }

再定义Publication抽象基类

public abstract class Publication
    {
        private bool published = false;
        private DateTime datePublished;
        private int totalPages;
        public Publication(string title,string publisher,PublicationType type)
        {
            if (string.IsNullOrWhiteSpace(title))
                throw new ArgumentNullException("title can not be null or white space");
            Title = title;
            if (string.IsNullOrWhiteSpace(publisher))
                throw new ArgumentNullException("publisher can not be null or white space");
            Publisher = publisher;
            Type = type;
        }
        public string Publisher { get; }
        public string Title { get; }
        public PublicationType Type { get; }
        public string CopyrightName { get; private set; }
        public int CopyrightDate { get; private set; }
        public int Pages
        {
            get { return totalPages; }
            set
            {
                if (value < 0) throw new ArgumentOutOfRangeException("The number of pages cannot be zero or negative");
                totalPages = value;
            }
        }
        public string GetPublicationDate()
        {
            if (!published) return "NYP";
            else return datePublished.ToString("d");
        }

        public void Publish(DateTime datePublished)
        {
            published = true;
            this.datePublished = datePublished;
        }

        /// <summary>
        /// 定义所有权名称和期限
        /// </summary>
        /// <param name="copyrightName"></param>
        /// <param name="copyrightDate"></param>
        ///<remarks></remarks>
        public void Copyright(string copyrightName,int copyrightDate)
        {
            if (string.IsNullOrEmpty(copyrightName))
                throw new ArgumentNullException("The copyright name can not be null or empty");
            CopyrightName = copyrightName;
            var currentYear = DateTime.Now.Year;
            if (copyrightDate < currentYear - 10 || copyrightDate > currentYear + 2)
                throw new ArgumentOutOfRangeException($"the copyright year must be validate");
            CopyrightDate = copyrightDate;
        }
        public override string ToString() => Title;
    }

Book表示一种类型的出版物,继承至Publication

public sealed class Book:Publication
    {
        public Book(string title,string guid,string author, string publisher) : base(title, publisher, PublicationType.Book)
        {
            if (string.IsNullOrEmpty(guid))
                throw new ArgumentNullException("GUID can not be null or empty");
            GUID = guid;
            Author = author;
        }

        public string GUID { get; }
        public string Author { get; }
        public decimal Price { get; private set; }
        public string Currency { get; private set; }

        /// <summary>
        /// 设置新的价格返回旧价格
        /// </summary>
        /// <param name="price"></param>
        /// <param name="currency"></param>
        /// <remarks></remarks>
        /// <returns>旧价格</returns>
        public decimal SetPrice(decimal price, string currency)
        {
            if (price < 0)
                throw new ArgumentOutOfRangeException("price can not be negative");
            var oldPrice = Price;
            Price = price;
            if (currency.Length != 3)
                throw new ArgumentNullException("the currency is a 3-character string");
            Currency = currency;
            return oldPrice;
        }
        public override bool Equals(object obj)
        {
            var book = obj as Book;
            return book == null ? false : GUID.Equals(book.GUID);
        }

        public override int GetHashCode() => GUID.GetHashCode();
        public override string ToString() => $"{(string.IsNullOrEmpty(Author) ? "" : Author + ",")}{Title}";
    }

在代码图中查看Book和Publication类的依赖关系和类中成员的引用关系

使用反射分别获取Book和Publication类的成员列表

 class Program
    {
        static void Main(string[] args)
        {
            var tPublication = typeof(Publication);
            var tBook = typeof(Book);
            var flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
            var membersPublication = tPublication.GetMembers(flags);
            var membersBook = tBook.GetMembers(flags);
            OutputClassInfo(tPublication, membersPublication);
            OutputClassInfo(tBook, membersBook);
            Console.ReadKey();
        }

        private static void OutputClassInfo(Type t, MemberInfo[] members)
        {
            Console.WriteLine($"Type {t.Name} has {members.Length} members:");
            foreach (var member in members)
            {
                var access = string.Empty;
                var stat = string.Empty;
                if (member is MethodBase method)
                {
                    if (method.IsPublic)
                        access = "Public";
                    else if (method.IsPrivate)
                        access = "Private";
                    else if (method.IsFamily)
                        access = "Protected";
                    else if (method.IsAssembly)
                        access = "Internal";
                    else if (method.IsFamilyOrAssembly)
                        access = "Protected Internal";
                    if (method.IsStatic)
                        stat = "Static";
                }
                var output = $"{member.Name} ({member.MemberType}): {access}{stat}, Declared by {member.DeclaringType}";
                Console.WriteLine(output);
            }
        }

从输出结果分析可得出:

1、Publication 和Book都隐式继承自基类

2、派生类只能有一个一个直接基类,当然可以隐式继承object

3、继承是可以传递的。

参考文档:https://docs.microsoft.com/zh-cn/dotnet/articles/csharp/tutorials/inheritance

 

以上是关于从设计基类及其派生类看继承关系的主要内容,如果未能解决你的问题,请参考以下文章

面向对象程序设计——概述,定义基类和派生类,虚函数

漫谈QWidget及其派生类(三)

关于基类和派生类之间的关系

c++继承总结

python程序设计——面向对象程序设计:继承

C++继承