《随笔十六》——C#中的 “ 虚函数 ”

目录

使用基类的引用

虚方法 和覆写方法

覆写标记为override 的方法

覆盖属性

构造函数初始化语句


使用基类的引用


派生类的实例由基类的实例加上派生类新增的成员组成。  派生类的引用指向整个类对象,包括基类部分。

如果有一个派生类对象, 就可以获取派生类对象的基类部分的引用。

 下面的代码示例 使用派生类对象的基类部分的引用来访问对象。

namespace HelloWorld_Console
{
    class SomeClass
    {// 当基类中的字段是私有的,那么在派生类中定义同名字段, 该字段不需要添new,如果是其它的访问修饰符就需要添加new
    // 以上规则对基类中的成员函数都一样
        int m_Field = 0;
      public void Print()
        {
            WriteLine("调用的是基类中的Print函数!");
        }
        public SomeClass()
        {
            WriteLine("调用的是基类中构造函数!");
        }
        public void Show()
        {
            WriteLine($"输出基类的m_Field的值:{m_Field}");
        }
    }
    class OtherClass : SomeClass
    {
        new public void Print()
        {
            WriteLine("调用的是派生类的Print函数!");
        }
       public OtherClass()
        {
            WriteLine("调用的是派生类中构造函数!");
        }
      new  public void Show()
        {
            WriteLine($"输出派生类m_Field的值:{m_Field}");
            base.Show(); //派生类中调用基类中的该函数
        }
        int m_Field = 99;  
    }
    class Program
    {
        static void Main(string[] args)
        {
            OtherClass myOtherClass = new OtherClass();
            SomeClass mySomeClass = (SomeClass)myOtherClass;  //该代码不会调用基类的构造函数
            myOtherClass.Show();
            mySomeClass.Print();
            ReadKey();
        }
    }
}

输出结果为:

调用的是基类中构造函数!
调用的是派生类中构造函数!
输出派生类m_Field的值:99
输出基类的m_Field的值:0
调用的是基类中的Print函数!

当派生类中隐藏了基类中的成员, 使用基类对象引用派生类对象时, 然后用基类对象访问同名成员,访问的是基类中的同名成员。

 


虚方法 和覆写方法


   使用虚函数可以使基类对象引用派生类对象时, 然后用基类对象访问同名成员,访问的是派生类中的同名成员。

可以使用基类引用调用派生类的方法,只需要满足下面的条件:

  • 派生类中的方法和基类的方法有相同的签名和返回类型。
  • 基类的方法使用virtual标识。
  • 派生类的方法使用override 标识。
namespace HelloWorld_Console
{
    class SomeClass
    {// 当基类中的字段是私有的,那么在派生类中定义同名字段, 该字段不需要添new,如果是其它的访问修饰符就需要添加new
    // 以上规则对基类中的成员函数都一样
        int m_Field = 0;
        public SomeClass()
        {
            WriteLine("调用的是基类中构造函数!");
        }
       virtual public void Print()
        {
            WriteLine("调用的是基类中的Print函数!");
        }
        virtual public void Show()
        {
            WriteLine($"输出基类的m_Field的值:{m_Field}");
        }
    }
    class OtherClass : SomeClass
    {
        override public void Print()
        {
            WriteLine("调用的是派生类的Print函数!");
        }
       public OtherClass()
        {
            WriteLine("调用的是派生类中构造函数!");
        }
        override  public void Show()
        {
            WriteLine($"输出派生类m_Field的值:{m_Field}");
            base.Show(); //派生类中调用基类中的该函数
        }
        int m_Field = 99;  
    }
    class Program
    {
        static void Main(string[] args)
        {
            OtherClass myOtherClass = new OtherClass();
            SomeClass mySomeClass = (SomeClass)myOtherClass;  //该代码不会调用基类的构造函数
            myOtherClass.Show();
            mySomeClass.Print();  //现在这里调用的就是派生类中的同名函数
            ReadKey();
        }
    }
}
输出结果为:

调用的是基类中构造函数!
调用的是派生类中构造函数!
输出派生类m_Field的值:99
输出基类的m_Field的值:0
调用的是派生类的Print函数!

 关于使用 virtual 和 override 关键字应注意的有:

  •  如果在基类中使用 virtual 关键字声明某个函数为虚函数, 那么在派生类中的原型相同的同名函数如果没有用override 标识,那么必须用 new 做前辍,否则的话, 派生类中的同名函数的签名(成员函数的签名由 名称和参数列表组成, 不包含返回类型。)必须不一样。
  • 基类中被覆盖的方法不能是 private 的,但是派生类可以覆盖基类中其他的访问性修饰符的,  而覆写方法一般来说是public的,  当然也可以是其他修饰符的任何一种, 如果是后者就不能再类外面访问该函数了, 只能间接调用。 所以说覆写方法一般来说是public的,这样在外面就可以调用了。
  • 不能覆写static 方法 或  构造函数  、析构函数, 每一个类都应有有自己的构造函数 和析构函数。
  • 方法 、事件、属性、索引器都可以被声明为 virtual  和 override ,  它们也可以被 隐藏(new)

覆写标记为override 的方法


 override 方法 可以在继承的任何层次上出现。

  • 当使用对象基类部分的引用调用一个覆写的方法时, 方法的调用被沿派生层次上溯执行, 一直标记为 override 的方法的最高派生类版本。
  • 如果在更高的派生级别有该方法的另外声明,但没有被标记为override, 那么他们就不会被调用。
namespace HelloWorld_Console
{
    class SomeClass
    {// 当基类中的字段是私有的,那么在派生类中定义同名字段, 该字段不需要添new,如果是其它的访问修饰符就需要添加new
    // 以上规则对基类中的成员函数都一样
        int m_Field = 0;
        public SomeClass()
        {
            WriteLine("调用的是基类中构造函数!\n");
        }
       virtual public void Print()
        {
            WriteLine("调用的是基类中的Print函数!\n");
        }
        virtual public void Show()
        {
            WriteLine($"输出基类的m_Field的值:{m_Field}\n");
        }
    }
    class OtherClass : SomeClass
    {
        override public void Print()
        {
            WriteLine("调用的是派生类OtherClass的Print函数!\n");
        }
       public OtherClass()
        {
            WriteLine("调用的是派生类OtherClass中构造函数!\n");
        }
        override  public void Show()
        {
            WriteLine($"输出派生类OtherClass中m_Field的值:{m_Field}\n");
            base.Show(); //派生类中调用基类中的该函数
        }
        int m_Field = 99;  
    }
    class SecondDerived : OtherClass
    {
        int m_Field = 999;
        override public void Print()
        {
            WriteLine("调用的是派生类SecondDerived的Print函数!\n");
        }
        public SecondDerived()
        {
            WriteLine("调用的是派生类SecondDerived中构造函数!\n");
        }
        override public void Show()
        {
            WriteLine($"输出派生类SecondDerived中的m_Field的值:{m_Field}\n");
            base.Show(); //派生类中调用基类中的该函数
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            OtherClass myOtherClass = new OtherClass();
            SecondDerived mySecondDerived = new SecondDerived();


            SomeClass mySomeClass = (SomeClass)myOtherClass;  //该代码不会调用基类的构造函数,或者说根本不会调用任何的构造函数
            myOtherClass.Show();
            mySecondDerived.Print();
            mySomeClass.Print();  //现在这里调用的就是派生类中的同名函数
            ReadKey();
        }
    }
}

输出结果为:
调用的是基类中构造函数!

调用的是派生类OtherClass中构造函数!

调用的是基类中构造函数!

调用的是派生类OtherClass中构造函数!

调用的是派生类SecondDerived中构造函数!

输出派生类OtherClass中m_Field的值:99

输出基类的m_Field的值:0

调用的是派生类SecondDerived的Print函数!

调用的是派生类OtherClass的Print函数!

 


●  把上面的 SecondDerived 类中有 override 函数 改成前辍 new,看有什么结果:

namespace HelloWorld_Console
{
    class SomeClass
    {// 当基类中的字段是私有的,那么在派生类中定义同名字段, 该字段不需要添new,如果是其它的访问修饰符就需要添加new
    // 以上规则对基类中的成员函数都一样
        int m_Field = 0;
        public SomeClass()
        {
            WriteLine("调用的是基类中构造函数!\n");
        }
       virtual public void Print()
        {
            WriteLine("调用的是基类中的Print函数!\n");
        }
        virtual public void Show()
        {
            WriteLine($"输出基类的m_Field的值:{m_Field}\n");
        }
    }
    class OtherClass : SomeClass
    {
        override public void Print()
        {
            WriteLine("调用的是派生类OtherClass的Print函数!\n");
        }
       public OtherClass()
        {
            WriteLine("调用的是派生类OtherClass中构造函数!\n");
        }
        override  public void Show()
        {
            WriteLine($"输出派生类OtherClass中m_Field的值:{m_Field}\n");
            base.Show(); //派生类中调用SomeClass中的该函数
        }
        int m_Field = 99;  
    }
    class SecondDerived : OtherClass
    {
        int m_Field = 999;
        new public void Print()
        {
            WriteLine("调用的是派生类SecondDerived的Print函数!\n");
        }
        public SecondDerived()
        {
            WriteLine("调用的是派生类SecondDerived中构造函数!\n");
        }
        new public void Show()
        {
            WriteLine($"输出派生类SecondDerived中的m_Field的值:{m_Field}\n");
            base.Show(); //派生类中调用SecondDerived的该函数
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            OtherClass myOtherClass = new OtherClass();
            SecondDerived mySecondDerived = new SecondDerived();


            SomeClass mySomeClass = (SomeClass)myOtherClass; 

            myOtherClass.Show();
            mySecondDerived.Show();
            mySomeClass.Show(); //这里调用的是OtherClass 的Show函数,如果OtherClass  函数也是new前辍,那么执行SomeClass 类中的该函数
            ReadKey();
        }
    }
}



输出结果为:
调用的是基类中构造函数!

调用的是派生类OtherClass中构造函数!

调用的是基类中构造函数!

调用的是派生类OtherClass中构造函数!

调用的是派生类SecondDerived中构造函数!

输出派生类OtherClass中m_Field的值:99

输出基类的m_Field的值:0

输出派生类SecondDerived中的m_Field的值:999

输出派生类OtherClass中m_Field的值:99

输出基类的m_Field的值:0

输出派生类OtherClass中m_Field的值:99

输出基类的m_Field的值:0


覆盖属性


namespace HelloWorld_Console
{
    class SomeClass
    {// 当基类中的字段是私有的,那么在派生类中定义同名字段, 该字段不需要添new,如果是其它的访问修饰符就需要添加new
     // 以上规则对基类中的成员函数都一样
       private int _myInt = 5;
        virtual public int MyProperty
        {
            set
            {
                if (value != 0)
                {
                    _myInt = value;
                }
            }
            get
            {
                return _myInt;
            }
        }
    }
    class MyDerivedClas: SomeClass
    {
        private int _myInt = 10;
        override public int MyProperty
        {
            set
            {
                if (value != 0)
                {
                    _myInt = value;
                }
            }
            get
            {
                return _myInt;
            }
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            MyDerivedClas derived = new MyDerivedClas();
            SomeClass myBase = new SomeClass();
            derived.MyProperty = 20; //设置属性的值
            myBase.MyProperty = 200;
            WriteLine($"输出derived.MyProperty的值 { derived.MyProperty}");
            WriteLine($"输出 myBase.MyProperty的值 {  myBase.MyProperty}");

            SomeClass mybc = (SomeClass)derived;  //基类对象引用派生类对象
            WriteLine($"\n输出derived.MyProperty的值 { derived.MyProperty}");
            WriteLine($"输出mybc.MyProperty的值 { mybc.MyProperty}");  //调用的是派生类中的属性
            ReadKey();
        }
    }
}

输出结果为:
输出derived.MyProperty的值 20
输出 myBase.MyProperty的值 200

输出derived.MyProperty的值 20
输出mybc.MyProperty的值 20

构造函数初始化语句


 默认情况下, 在构造对象时,将调用的是基类的无参数构造函数。 如果希望派生类使用一个指定的基类构造函数而不是无参数构造函数,必须在构造函数初始化语句中指定它。

有两种形式的构造函数初始化语句:

  • 使用关键字base 并指明使用哪一个基类构造函数。
  • 使用关键字this 并指明应该使用当前类的哪个类构造函数 —— (相当于C++中的委托构造函数)。
  • 注意: 在基类参数列表中的参数必须在类型和顺序方面与已定的基类构造函数的参数列表相匹配。

《随笔十六》——C#中的 “ 虚函数 ”_第1张图片

namespace HelloWorld_Console
{
   class MyClass
    {
        readonly int firstVar;
        readonly  double secondVar;
     
         int UserIdNumber;
        MyClass()
        {
            firstVar = 20;
            secondVar = 400.3;
            WriteLine("私有的MyClass 构造函数被调用!");
        }
        public MyClass(int firstName):this()
        {
            UserIdNumber = firstName;
            WriteLine("公有的MyClass 构造函数被调用!");
        }
        public int getValue
        {
            get
            {
                return UserIdNumber;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass(55);
            WriteLine($"输出UserIdNumber的值:{mc.getValue}");


            ReadKey();
        }
    }
}

输出结果为:

私有的MyClass 构造函数被调用!
公有的MyClass 构造函数被调用!
输出UserIdNumber的值:55

 

 

你可能感兴趣的:(C#中的随笔,C#,虚函数)