zoukankan      html  css  js  c++  java
  • C#

    今天在公司,同事在学习C#,刚好学到了继承这一章节,但是对于new和override并不是很了解,所以就跑来问小弟我,好死不死,刚好问到这个几百年都没什么在用的东西,所以紧急恶补一下后,决定还是把new和override写到Blog来好了,以防未来又被问倒…( 至少未来被问到,又忘记的时候可以说,我Blog里面有写XDD )。

    override

    当然,这部分不是这篇文章的重点,相信学过Java或是常写C#等等OO语言的人,对这个东西应该一点也不陌生了,我们就直接来看看范例吧;下面这个范例其实很简单,简单的说,就是Class2继承Class1,也就是说父类别为Class1,子类别为Class2,换言之Class1为基底类别,Class2为衍生类别( 好吧,我也搞不懂为什么会出现那么多名词,反正就是Class2继承Class1就是了),而有时因为继承的物件( Class2 )的方法( Test )会和父类别( Class1 )的方法( Test )有不一样的处理逻辑,例如我同事就举例说,父类别是鸟,子类别是企鹅( 至于企鹅到底是不是鸟类,就不在这边讨论了),而鸟会有一个飞的方法,可以飞得远远的,但是子类别的企鹅,虽然也有飞的方法,但可能只能离地几公分;总之,子类别的方法名称虽然可能会和父类别的方法名称相同,但其实里面的逻辑不同,而这个时候,我们就可以在父类别的方法里面加上Virtual,子类别里面的方法加上override,来进行方法逻辑复写的动作,如下程式码。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        public class Class1
        {
            //如果沒有加上Virtual,子類別又override這個方法
            //編譯器就會生氣喔!
            public virtual void Test()
            {
                Console.WriteLine("Class1.Test()");
            }
        }
    
        public class Class2 : Class1
        {
            //這裡使用override來複寫
            public override void Test()
            {
                Console.WriteLine("Class2.Test()");
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Class2 c = new Class2();
                c.Test();//理所當然會出現Class2.Test()
    
                //讓畫面可以暫停。
                Console.ReadLine();
            }
        }
    }

    所以到这边,画面上,会出现的是Class2.Test,也就是说,实际上是执行Class2的Test方法。

    new

    接下来,我们看一下下面这个程式码,我相信使用者一定不是故意的没写override,因为初学者可能不是很懂,小弟以前就干过这种事情,直接写了两个Class,也没override 。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        public class Class1
        {
            //故意沒加上Virtual
            public void Test()
            {
                Console.WriteLine("Class1.Test()");
            }
        }
    
        public class Class2 : Class1
        {
            //這裡也不使用override關鍵字來複寫
            public void Test()
            {
                Console.WriteLine("Class2.Test()");
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Class2 c = new Class2();
                c.Test();//結果還是會出現Class2.Test()
    
                //讓畫面可以暫停。
                Console.ReadLine();
            }
        }
    }

    这时,编译器就会不爽,如下图。

    ooo隐藏了继承的成员xxx,如果是刻意要隐藏,请使用new关键字。

    image

    好,虽编译器不爽,但还是可以过,为什么呢?,我们可以从MSDN找到这串字

    If the method in the derived class is not preceded by new or override keywords, the compiler will issue a warning and the method will behave as if the new keyword were present.

    用小弟的破英文,稍微翻一下,意思就是说,假如这个方法没有new或是override这个两个关键字,伟大的编译器,就会很不爽地提醒你,但还是会很捞叨的帮你加上new这个关键字的效果。

    所以就算不加上new和override还是可以正常编译,就是因为伟大的编译器帮你处理了,但没加上new这个关键字,编译器还是会很担心,怕使用者不知道它会自动地加上这个关键字,所以会冒出这个讯息来告知使用者"你现在的父类别方法xxx会隐藏起来,而实际会使用子类别的ooo方法,如果你真的是要这样的效果,请加上关键字new,来让编译器的我知道";听起来还不错啊,编译器其实不是不爽,而是关心你!,但目前new关键字和override关键字,看起来的效果一样阿,所以未来我们就可以不用加上new和override了吗!?毕竟加上new和override,看起来效果都差不多阿?,我承认我也有这样想过XDD,new和override虽然目前看起来的结果都一样,但真正的涵义是不同的,不然就不会出现这篇文章了XDD。

    隐藏和复写

    为什么那个警告是要用隐藏这字眼,而不是复写这词呢?,我们先看一下下面的程式码,这也是今天同事问我的问题,我们一样有Class1和Class2,但是在Class1上多加一个PreTest的方法,并在这个方法里面呼叫Test(),而Class2里面没有写PreTest方法,只有利用new关键字,来将Class1的Test方法隐藏起来,但毕竟Class2是继承Class1,所以实际上在执行时期,还是可以执行PreTest方法。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        public class Class1
        {
            //多加一個方法
            public void PreTest()
            {
                Console.WriteLine("PreTest()");
                Test();
            }
            //
            public void Test()
            {
                Console.WriteLine("Class1.Test()");
            }
        }
    
        public class Class2 : Class1
        {
            //使用new關鍵字,讓編譯器開心一點。
            public new void Test()
            {
                Console.WriteLine("Class2.Test()");
            }
    
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                
                Class2 c = new Class2();
                //改成呼叫c.PreTest()
                c.PreTest();//出現卻是Class1.Test()!!?
    
                //讓畫面可以暫停。
                Console.ReadLine();
            }
        }
    }

    好,结果出来了,答案却是Class1.Test(),这的确让我很错愕XDD,原本想说,这是Class2的实体,所以虽然透过Class1的PreTest来呼叫Test方法,但实际上应该也是要呼叫到Class2的Test方法阿,怎么会是呼叫到Class1的Test方法呢!?答案很简单,就出在new这个关键字;我们回过头来看看,为什么编译器警告时要用隐藏这个字眼,就是因为new的情况,只是会把Class1的Test隐藏起来,但实际上,无论是Class1或是Class2都还存着各自的Test方法;而C#的机制,当继承的Class2并无特别的撰写PreTest方法,所以会回到Class1里面去执行PreTest,当执行到Test()这行的时候,又因为Class2不是使用Override的方式来复写Class1的Test方法(也就是说实际上存着两个Test方法),所以就会直接使用Class1的方法;所以如果真的要执行Class2的Test方法,就必须要使用Override,也就是复写真正的含意,如下。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        public class Class1
        {
            //多加一個方法
            public void PreTest()
            {
                Console.WriteLine("PreTest()");
                Test();
            }
            //
            public virtual void Test()
            {
                Console.WriteLine("Class1.Test()");
            }
        }
    
        public class Class2 : Class1
        {
            //使用override
            public override void Test()
            {
                Console.WriteLine("Class2.Test()");
            }
    
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                
                Class2 c = new Class2();
                //改成呼叫c.PreTest()
                c.PreTest();//出現的是Class2.Test()
    
                //讓畫面可以暫停。
                Console.ReadLine();
            }
        }
    }

    这就是override和new最大的差异点。

    多型后的override

    这时候,可能因为读到后面章节,教到多型了( 如果都很懂得大大们,请体谅这些梗),所以我们就使用多型的方式,修改一下程式码,并使用override,如下。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        public class Class1
        {
            //因為要使用override所以加上Virtual
            public virtual void Test()
            {
                Console.WriteLine("Class1.Test()");
            }
        }
    
        public class Class2 : Class1
        {
            //使用override關鍵字來複寫
            public override void Test()
            {
                Console.WriteLine("Class2.Test()");
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                //我們用個多型吧!
                Class1 c = new Class2();
                c.Test();//結果並不易外,出現的是Class2.Test()
    
                //讓畫面可以暫停。
                Console.ReadLine();
            }
        }
    }

    这边当然不意外的是出现Class2.Test,因为Class2复写了Class1的Test方法,所以呼叫c.Test()的时候,因为知道Class2有加上override关键字,所以实际是呼叫Class2的Test方法。

    多形后的new

    现在,我们在多形,并配合new,如下程式码。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        public class Class1
        {
            //使用new,就可以不加上virtual
            public void Test()
            {
                Console.WriteLine("Class1.Test()");
            }
        }
    
        public class Class2 : Class1
        {
            //使用new關鍵字
            public new void Test()
            {
                Console.WriteLine("Class2.Test()");
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                //我們用個多型吧!
                Class1 c = new Class2();
                c.Test();//結果超易外,出現的是Class1.Test()
    
                //讓畫面可以暫停。
                Console.ReadLine();
            }
        }
    }

    吓到了吧,小弟我第一次看的确吓到XDD,这次出来的结果又不是原本预期的Class2.Test(),就如前面说的,new只是会隐藏Class1的Test,所以又会存在着两个Test方法,而C#预设的情况下,会先处理变数型别的方法,例如上面的程式码,变数型别为Class1,所以预设如果执行c.Test(),则会进行Class1的Test方法,除非Class2的Test有明确的加上override关键字,这时候,才会进行Class2的Test方法。

    为什么要搞得那么复杂

    讲到这边,差不多就把new和override可能发生的状况都讲过一次了,但为何C#要把事情弄得那么复杂,其实凡事必有因,会有这样的机制,是为了扩充上的弹性;假设目前的架构是Class1里面没有任何方法,而Class2里面有写一个Test方法,过了一段时间,可能Class1也加上了Test方法,这时候Class1和Class2也都有了Test方法了,此时,就算编译,依然是没有任何的问题的,如下。

    Class1 c1 = new Class1();//不影響,還是呼叫class1的Test
    c1.Test();
    Class2 c2 = new Class2();//不影響,還是呼叫Class2的Test
    c2.Test();
    Class1 c3 = new Class2();//不影響,在Class1加上Test之前,是不可能這樣寫的
    c3.Test();
    Class2 c4 = new Class1();//怎麼可能會有這種寫法勒...

    也因为这样,所以C#才会有new和override的一个差异,不过现实上,小弟我还是很少用到new就是了。

    结论

    写了那么多东西,基本的原则就是,如果是override,就和一般的继承没什么两样,但若是用到new,会先看变数型别来决定呼叫的是哪里的方法,如果这个变数型别里面的方法是继承而来的,就会回到父层去执行,而如果父层里面还有呼叫其他方法,只要子层没有override,则都会执行父层的方法。

    后记

    这篇写了整整三个小时,重新编排了三次= =,目的也是希望能看得比较懂XDD,这个东西也是C#很眉眉角角的东西,现实生活上,应该是很少用到new,但也难讲有哪天会遇到这种的设计方式,所以在这边纪录一下,希望未来忘记,还有地方可以找的到XDD。

    原文:http://www.dotblogs.com.tw/skychang/archive/2012/05/10/72114.aspx

  • 相关阅读:
    自定义udf添加一列
    spark执行命令 监控执行命令
    R链接hive/oracle/mysql
    [Hive_6] Hive 的内置函数应用
    [Hive_add_6] Hive 实现 Word Count
    [Hive_add_5] Hive 的 join 操作
    【爬坑】远程连接 MySQL 失败
    [Hive_add_4] Hive 命令行客户端 Beeline 的使用
    [Hive_5] Hive 的 JDBC 编程
    [Hive_add_3] Hive 进行简单数据处理
  • 原文地址:https://www.cnblogs.com/tianzhenyun/p/3833747.html
Copyright © 2011-2022 走看看