C#中的override和new关键字

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
大数据开发治理平台 DataWorks,不限时长
简介: 在 C# 中,派生类可以包含与基类方法同名的方法。基类方法必须定义为 virtual。如果派生类中的方法前面没有 new 或 override 关键字,则编译器将发出警告,该方法将有如存在 new 关键字一样执行操作。如果派生类中的方法前面带有 new 关键字,则该方法被定义为独立于基类中的方法。如果派生类中的方法前面带有 override 关键字,则派生类的对象将调用该方法,而不是调用基类方法。可以从派生类中使用 base 关键字调用基类方法。

问题1: 虚方法


首先,看下面的代码。
namespace CSharpTest
{
    class A
    {
        public void fun()
        {
            Console.WriteLine("这是父类方法");
        }
    }

    class B : A
    {
        public void fun()
        {
            Console.WriteLine("这是子类方法");
        }
    }

    class Test
    {
        public static void Main()
        {
            A a = new A();
            a.fun();
            a = new B();
            a.fun();
        }
    }
}

猜猜结果是什么?
如果对Java熟悉的朋友,可能会认为是结果:

这是父类方法
这是子类方法

但是其实运行结果是:

这是父类方法
这是父类方法

这是因为Java中的类方法默认是虚函数(虽然Java中没有这个叫法),子类函数会默认覆盖父类的同名函数(Java后来提供了@override注解)。然而C#中必须使用virtual关键字显示声明该函数是虚函数,然后在子类中使用override关键字重写父类方法,这才真正实现了对父类方法的重写,才能实现多态(C++中的多态就是使用虚函数实现的,而且和C#一样必须使用virtual关键字显示声明)。

所以,要输出结果为:

这是父类方法
这是子类方法

需要修改代码如下:

namespace CSharpTest
{
    class A
    {
        public virtual void fun()
        {
            Console.WriteLine("这是父类方法");
        }
    }

    class B : A
    {
        public override void fun()
        {
            Console.WriteLine("这是子类方法");
        }
    }

    class Test
    {
        public static void Main()
        {
            A a = new A();
            a.fun();
            a = new B();
            a.fun();
        }
    }
}

即给父类的方法添加virtual关键字,给子类的同名方法添加override关键字。


问题2:override和new关键字


首先看下面的代码:
namespace CSharpTest
{
    class C
    {
        public virtual void fun()
        {
            Console.WriteLine("这是一个虚方法");
        }
    }
    class C1 : C
    {
        public override void fun()
        {
            Console.WriteLine("使用override关键字修饰的方法");
        }
    }
    class C2 : C
    {
        public new void fun()
        {
            Console.WriteLine("使用new关键字修饰的方法");
        }
    }

    class Test
    {
        public static void Main()
        {
            C c1 = new C1();
            c1.fun();
            C c2 = new C2();
            c2.fun();
        }
    }
}

猜猜上面的代码输出结果?
正确的结果是:

使用override关键字修饰的方法
这是一个虚方法

为什么使用关键字new修饰的方法,调用的是父类的方法呢?
是不是很奇怪?为什么使用override关键字的子类方法被调用了,而使用new关键字的子类方法没有被调用。
首先看看override关键字:override方法为从基类继承的成员提供新的实现。以override声明重写的方法被称为被重写的基类方法,被重写的基类方法必须具有与重写方法相同的签名。非虚方法或者静态方法不能被重写,被重写的基类必须是virtual、abstract或者override的。override声明不能改变虚方法的可访问性,override方法和virtual方法必须具有相同的访问级别修饰符。不能使用下列修饰符修饰重写方法:new、static、virtual和abstract。
new关键字:new修饰符用来明确地隐藏由基类继承而来的成员。要隐藏继承而来的成员,可以在派生类中共用相同的名称并用new修饰符修饰它。
下面来分析我们的程序:
c1.fun();因为子类C1使用override关键字重写了父类的方法,基类C和子类C1都具有fun()方法,所以c1.fun()会动态调用C1的fun()方法而不是父类的。
c2.fun();子类C2使用new关键字隐藏了父类的方法,相当于子类中的fun()方法是直接继承自父类的。而子类中使用new关键字声明的fun()方法是另一个方法,只是恰巧与子类的fun()方法同名而已(是不是有些糊涂了)。所以 c2.fun()会调用父类的fun()方法,要想调用C2的fun()方法必须吧c2强制转换为C2.


下面看看微软官方的文档解释:
C# 语言经过专门设计,以便不同库中的基类与派生类之间的版本控制可以不断向前发展,同时保持向后兼容。这具有多方面的意义。例如,这意味着在基类中引入与派生类中的某个成员具有相同名称的新成员在 C# 中是完全支持的,不会导致意外行为。它还意味着类必须显式声明某方法是要重写一个继承方法,还是一个隐藏具有类似名称的继承方法的新方法。
在 C# 中,派生类可以包含与基类方法同名的方法。
基类方法必须定义为 virtual。
如果派生类中的方法前面没有 new 或 override 关键字,则编译器将发出警告,该方法将有如存在 new 关键字一样执行操作。
如果派生类中的方法前面带有 new 关键字,则该方法被定义为独立于基类中的方法。
如果派生类中的方法前面带有 override 关键字,则派生类的对象将调用该方法,而不是调用基类方法。
可以从派生类中使用 base 关键字调用基类方法。
override、virtual 和 new 关键字还可以用于属性、索引器和事件中。
默认情况下,C# 方法为非虚方法。如果某个方法被声明为虚方法,则继承该方法的任何类都可以实现它自己的版本。若要使方法成为虚方法,必须在基类的方法声明中使用 virtual 修饰符。然后,派生类可以使用 override 关键字重写基虚方法,或使用 new 关键字隐藏基类中的虚方法。如果 override 关键字和 new 关键字均未指定,编译器将发出警告,并且派生类中的方法将隐藏基类中的方法。

目录
相关文章
|
2月前
|
C#
30.C# 关键字 this初步学习
30.C# 关键字 this初步学习
15 1
|
2月前
|
C#
28.c#关键字base初步学习
28.c#关键字base初步学习
11 0
|
2月前
|
C#
27.c#关键字sealed修饰类
27.c#关键字sealed修饰类
12 0
|
3月前
|
SQL 开发框架 .NET
EntityFramework数据持久化复习资料3、C#拓展方法与yield关键字使用
EntityFramework数据持久化复习资料3、C#拓展方法与yield关键字使用
23 0
|
4月前
|
开发框架 .NET 编译器
C# 9.0中的静态匿名函数:引入static关键字的新用法
【1月更文挑战第15天】C# 9.0为匿名函数带来了一个新的修饰符static,允许开发者明确指定匿名函数不会捕获其包含作用域中的任何变量。这一特性增强了代码的性能和可读性,同时减少了因不小心捕获变量而导致的潜在错误。本文将详细探讨C# 9.0中静态匿名函数的语法、使用场景以及它们如何影响代码的性能和安全性。
|
2月前
|
存储 Java C++
31.C#:关键字static
31.C#:关键字static
15 1
|
2月前
|
C#
29.C#关键字throw初步学习
29.C#关键字throw初步学习
19 0
|
3月前
|
存储 编译器 C#
C#关键字常见面试题
C#关键字常见面试题
|
4月前
|
C# 数据安全/隐私保护 开发者
C# 9.0中的Init关键字:Init-only Setters的新篇章
【1月更文挑战第12天】本文介绍了C# 9.0中引入的Init关键字,该关键字允许创建仅在对象初始化时可设置属性的setter。通过Init-only setters,开发者能够更加灵活地控制对象属性的赋值时机,提高代码的可维护性和安全性。文章详细解释了Init关键字的使用方法、适用场景以及与传统setter的区别,并探讨了其在实际开发中的潜在影响。
|
5月前
|
C# Kotlin
C#is、as关键字及获取当前活动窗体的实例
这篇日志记录一下C#中is关键字及as关键字的用法。 Is :判断检查对象是否与给定类型兼容 As :将对象转换为指定类型(强转),就跟(int )这样的用法是一样的。 获取当前窗体的活动子窗体。
23 0