C#中的override和new关键字

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
实时计算 Flink 版,5000CU*H 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
简介: 在 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 关键字均未指定,编译器将发出警告,并且派生类中的方法将隐藏基类中的方法。

目录
相关文章
|
3月前
|
开发框架 Cloud Native .NET
10 个 C# 关键字和功能
10 个 C# 关键字和功能
73 8
|
5月前
|
C#
C#中的overload,overwrite,override的语义区别
以上概念是面向对象编程中实现多态性和继承的重要基石。理解它们之间的区别对于编写清晰、可维护的代码至关重要。
173 7
|
5月前
|
C# 索引
C#中的virtual和override关键字
C#中的virtual和override关键字
54 3
|
5月前
|
开发框架 .NET C#
【Azure Developer】C# / .NET 静态函数中this关键字的作用
【Azure Developer】C# / .NET 静态函数中this关键字的作用
|
6月前
|
开发框架 .NET C#
【Azure Developer】C# / .NET 静态函数中this关键字的作用
在C#中,`this`关键字用于扩展方法,允许向已有类型添加功能而不修改其源代码。扩展方法必须在静态类中定义,且第一个参数使用`this`修饰,如`public static XElement AcquireElement(this XContainer container, string name, bool addFirst = false)`。这种方式增强了代码的可读性和类型的安全性,尤其在处理第三方库时。
|
5月前
|
C# 计算机视觉
C#中out关键字
C#中out关键字
72 0
|
5月前
|
编译器 C# 计算机视觉
C#中的ref关键字
C#中的ref关键字
74 0
|
2月前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
44 3
|
20天前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
76 12
|
2月前
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
75 4