C# 学习笔记 多态

Posted 不咸不要钱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 学习笔记 多态相关的知识,希望对你有一定的参考价值。

C# 学习笔记(6) 多态

面向对象三大特性,封装、继承和多态,前两个很容易理解,但是多态就不好描述了,每个人对多态的看法可能都不一样,个人认为多态就是通过继承实现的不同对象调用相同方法,表现出不同行为。举个例子,假设有一个动物类,里面有个吃东西的方法,然后猫类和人类都从动物类继承过来吃东西这个方法,但是人和猫吃东西肯定有区别的,因此会通过种种手段,分别实现人类和猫类自己的吃东西方法。通过继承,人和猫(不同对象)调用吃东西(相同方法),表现出不同行为(吃东西有区别)

重载

C#中允许方法名重复(方法重载),不过要求方法参数要么个数不同,要么类型不同。

        //三个方法名字都叫Learn 方法1、2参数个数相同但是类型不同 方法1、3参数类型相同但是个数不同
        public void Learn(string subject, int time)
        {
            Console.WriteLine("学习{0}科目, 学习时长:{1}", subject, time);
        }
        public void Learn(string subject1, string subject2)
        {
            Console.WriteLine("学习{0}科目, 学习{1}科目", subject1, subject2);
        }
        public void Learn(string subject, int time, float ratio)
        {
            Console.WriteLine("学习{0}科目, 学习时长:{1}, 学习效率:{2}", subject, time, ratio);
        }

隐藏

重名时编译器会提示警告,但是可以在子类重名的属性或者方法前添加new关键字,这样子类的属性或者方法就会屏蔽掉父类的重名属性或者方法,这时候如果想在子类中调用被屏蔽掉的父类属性或方法,需要使用关键字base. 调用

    class Program
    {
        static void Main(string[] args)
        {
            Student student = new Student();
            student.Eat();
            Console.ReadKey();
        }
    }


    public class Person
    {
        public string Name;
        public int Age;
        public bool Gender;
        
        public Person()
        {
            Console.WriteLine("人类构造函数");
        }
        public Person(int age)
        {
            this.Age = age;
            Console.WriteLine("人类构造函数 人类年纪:{0}", this.Age);
        }
        public void Eat()
        {
            Console.WriteLine("父类吃饭吃两碗");
        }
    }

    class Student : Person
    {
        public Student():base(50)
        {
            Console.WriteLine("子类构造函数");
        }
        public new void Eat()
        {
            Console.WriteLine("子类吃饭吃两碗");
        }
    }
  • 上面代码会输出:

      人类构造函数 人类年纪:50
      子类构造函数
      子类吃饭吃两碗
    

虚方法

虚方法在父类中方法前面加关键字 virtual 这样就将该方法标记为虚方法,子类如果想要重写该虚方法,可以写一个同名方法,在方法前加关键字 override ,如果不重写该方法,则子类可以调用父类的虚方法。注意虚方法和上面的隐藏很像,但是不同的是,使用里氏转换将子类装进父类,然后直接调用被子类隐藏的方法,会发现调用的是父类的方法,而该方法如果被子类重写则实际调用的是子类重写的方法

class Program
    {
        static void Main(string[] args)
        {
            //里氏转换 子类可以放在父类对象里
            Person student = new Student();
            student.Eat();

            Person maleStudent = new MaleStudent();
            maleStudent.Eat();
            Console.ReadKey();
        }
    }


    public class Person
    {
        public string Name;
        public int Age;
        public bool Gender;

        public Person()
        {
            Console.WriteLine("人类构造函数");
        }
        public Person(int age)
        {
            this.Age = age;
            Console.WriteLine("人类构造函数 人类年纪:{0}", this.Age);
        }
        public virtual void Eat()
        {
            Console.WriteLine("人类吃饭吃两碗");
        }
    }

    class Student : Person
    {
        public new void Eat()
        {
            Console.WriteLine("学生吃饭吃两碗");
        }
    }

    class MaleStudent : Person
    {
        public override void Eat()
        {
            Console.WriteLine("男学生吃饭吃两碗");
        }
    }
  • 上面代码会输出:

      人类构造函数 人类年纪:50
      子类构造函数
      人类吃饭吃两碗
      人类构造函数
      男学生吃饭吃两碗
    

由上面输出可以看出 重写和隐藏的区别 当使用子类装进父类后,直接调用重写的方法,该方法依然是子类的方法,而隐藏的则是父类的方法。

抽象类

当父类中的方法不知道如何去实现,也不需要被实例化的时候,可以考虑将父类写成抽象类,将方法写成抽象方法。比如说需要实现一些动物类和植物类,这时就可以抽象出一个父类,生命类,生命从定义上就是一个抽象的,而且也不需要实例化该类,就可以将该类定义为抽象类,生命类有一个方法————活着,至于具体怎么活,每种生物都有自己的活法,因此这里也没办法实现,可以将活着定义为抽象方法。

  1. 抽象成员必须标记为abstract,并且不能有任何实现。
  2. 抽象成员必须在抽象类中。
  3. 抽象类不能被实例化
  4. 子类继承抽象类后,必须把父类中的所有抽象成员都重写。(除非子类也是一个抽象类,则可以不重写)
  5. 抽象成员的访问修饰符不能是private
  6. 在抽象类中可以包含实例成员。
    并且抽象类的实例成员可以不被子类实现
  7. 抽象类是有构造函数的。但不能被实例化。
  8. 如果父类的抽象方法中有参数,那么。继承这个抽象父类的子类在重写父类的方法的时候必须传入对应的参数。如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候 也必须要传入返回值。
class Program
    {
        static void Main(string[] args)
        {
            Animal animal = new Animal();
            animal.GetAge();
            animal.Live();
            Console.ReadKey();
        }
    }

    /// <summary>
    /// 抽象类 生物类
    /// </summary>
    public abstract class Biology
    {
        //实例成员
        public int Age;
        public void GetAge()
        {
            Console.WriteLine("活了{0}年", this.Age);
        }

        /// <summary>
        /// 抽象方法 活着
        /// </summary>
        public abstract void Live();
    }

    public class Animal : Biology
    {
        public override void Live()
        {
            Console.WriteLine("Animal重写的Live");
        }
        public void Sleep()
        {
            Console.WriteLine("Animal Sleep");
        }
        public int Sleep(int time)
        {
            Console.WriteLine("Animal在{0}点Sleep", time);
            return time;
        }
        public virtual void EatFood()
        {
            Console.WriteLine("Animal EatFood");
        }
    }
  • 上面代码会输出:

      活了0年
      Animal重写的Live
    

接口

接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 “是什么” 部分,派生类定义了语法合同 “怎么做” 部分。比如说支付宝提供了一个接口,你只要将这个接口实现,就可以调用支付宝的功能。抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。

  1. 一个类能同时实现多个接口,还能在实现接口的同时再继承其他类,并且接口之间也可以继承。
  2. 接口中的成员不允许使用 public、private、protected、internal 访问修饰符。
  3. 接口中的成员不允许使用 static、virtual、abstract、sealed 修饰符。
  4. 在接口中不能定义字段,可以定义方法、自动属性、索引器
  5. 在接口中定义的方法不能包含方法体。
interface IMyInterface1
    {
        int Age
        {
            get;
            set;
        }
        void IMyInterface1Test();
    }
    interface IMyInterface2
    {
        void IMyInterface2Test();
    }

    class Test : IMyInterface1, IMyInterface2
    {
        int _age;
        public int Age
        {
            //属性实现 本质上也是一种方法
            get { return _age;  }
            set { _age = value; }
        }
        //方法实现
        public void IMyInterface1Test()
        {
            Console.WriteLine("接口1实现");
        }
        public void IMyInterface2Test()
        {
            Console.WriteLine("接口2实现");
        }
    }

以上是关于C# 学习笔记 多态的主要内容,如果未能解决你的问题,请参考以下文章

C#多态性学习,虚方法抽象方法接口等用法举例

学习笔记:python3,代码片段(2017)

Java学习笔记(十八):多态

java学习笔记之面向对象多态

(Object-C)学习笔记 --OC的内存管理封装继承和多态

Python学习笔记-面向对象进阶封装多态继承