【问题标题】:Using the 'new' modifier in C#在 C# 中使用“新”修饰符
【发布时间】:2014-05-13 15:03:08
【问题描述】:

我读到new 修饰符隐藏了基类方法。

using System;

class A
{
    public void Y()
    {
        Console.WriteLine("A.Y");
    }
}

class B : A
{
    public new void Y()
    {
        // This method HIDES A.Y.
        // It is only called through the B type reference.
        Console.WriteLine("B.Y");
    }
}

class Program
{
    static void Main()
    {
        A ref1 = new A(); // Different new
        A ref2 = new B(); // Polymorpishm
        B ref3 = new B();

        ref1.Y();
        ref2.Y(); //Produces A.Y line #xx
        ref3.Y();
    }
}

为什么ref2.Y(); 产生A.Y 作为输出?

这是简单的多态,基类对象指向派生类,所以应该调用派生类函数。我实际上是 Java 兼 C# 编码器;这些概念让我大吃一惊。

当我们说new隐藏基类函数时,这意味着base类函数不能被调用,据我所知,这就是隐藏的意思.

ref

【问题讨论】:

  • 避免使用new 修饰符。它引入了一个具有相同名称的不相关成员(在像这里这样的方法的情况下是签名),但原始(继承)成员也在那里。这会导致混乱。在您的示例中,B 实例具有 两个 实例方法,称为 Y()。使用ref3.Y(),您会看到B 中声明的那个优先于继承的那个。这就是所有“隐藏”的意思。如果您真的想要一个不相关的方法,请使用未使用的名称并避免使用new。如果您想要多态性,请按照答案的建议使用override
  • @JeppeStigNielsen ref3.Y() 是纯 B 类调用,我没有提到 Y() 是虚拟的,所以它根本没有继承,如果我说猫隐藏了孩子,那就意味着猫在孩子面前,类似地,如果派生类函数隐藏了基类函数,那么派生类函数应该被调用,因为基类一被隐藏
  • 肯定会继承非虚方法。
  • @JeppeStigNielsen 我明白了,可能是太多的 Java 编码毁了我的概念。还是谢谢你
  • 这个“隐藏”术语的唯一一点是:如果一个类型有两个外观相同的成员,一个是从某个基类继承的,另一个是在这个类中声明的,通过从更专业的类中选择方法来解决歧义。这就是它的全部。 new 修饰符实际上并没有改变任何东西(除了抑制编译时警告)。没有new(如果您容忍编译器警告),您将获得完全相同的行为。 new 只是为了告诉编译器“我知道我在做一些愚蠢的事情,放松一下”。

标签: c# .net oop polymorphism new-operator


【解决方案1】:

在 C# 中,默认情况下方法不是虚拟的(与 Java 不同)。所以ref2.Y()方法调用不是多态的。

要从多态中受益,您应该将A.Y() 方法标记为virtual,并将B.Y() 方法标记为override

new 修饰符所做的只是隐藏从基类继承的成员。这就是您的 Main() 方法中真正发生的事情:

A ref1 = new A();
A ref2 = new B();
B ref3 = new B();

ref1.Y(); // A.Y
ref2.Y(); // A.Y - hidden method called, no polymorphism
ref3.Y(); // B.Y - new method called

【讨论】:

  • 您能否也解释一下new 修饰符的实际作用? Jeppe Stig Nielsen 的评论包含如此宝贵的信息。
  • @Sergey 所以如果我做 ref2.(dot) 我会得到 B 类中定义的所有成员和变量,对吧?它仍然指向该类的对象吗?
  • @user1765876 ref2编译时类型A。但是,对象ref2运行时类型B。执行ref2.MemberThatDoesNotExistInA 是编译时错误。做ref2.MemberFromA 是可以的。如果该成员是非虚拟的(没有virtualabstractoverride 修饰符),您可以从A 类中获得实现。只有当成员是 virtual 时,您才能获得覆盖的实现(如果 B 提供了一个)。
  • @JeppeStigNielsen 所以基本上我不能这样做 ref2.MemberfromB... 其中 MemberfromB 是非继承变量或方法。
  • @user1765876 就像你不能说object obj = XXX; obj.TransferMoney(1000m);一样。这里有静态类型; object 不包含名为 TransferMoney 的方法。可能obj 的编译时类型实际上包含这样一个方法,但是编译器怎么知道呢?它不记得这是来自XXX,它可能不知道XXX 返回什么。
【解决方案2】:

(只是补充其他答案,我自己cmet。)

当使用new 时,类(或结构体或接口)将有两个 外观相同的成员,一个从基类型继承,一个由类型本身声明。避免这种情况!

重要提示:仅仅因为您说new,您不会“删除”旧成员。它仍然存在,并且可以很容易地调用。 new 成员不会替换继承的成员。它是一个不相关的成员,恰好有相同的名字。

在一个类型中有两个或多个成员看起来相同是不好的。它会导致混乱。考虑代码:

interface IOne
{
  void Y();
}
interface ITwo
{
  void Y();
}
interface IBoth : IOne, ITwo
{
}
class Test
{
  static void M(IBoth obj)
  {
    obj.Y();  // must not compile!
  }
}

IBoth 类型有两个看起来相同的成员(都是继承的)。根据obj 的具体类,这些方法可能有不同的实现。电话obj.Y() 是模棱两可的。您必须将 obj 强制转换为基本接口之一才能解决此问题。

然后考虑这段代码:

interface IBase
{
  void Y();
}
interface IDerived : IBase
{
  /* new */ void Y();
}
class Test
{
  static void M(IDerived obj)
  {
    obj.Y();  // allowed; IDerived has two Y, but one hides the other
  }
}

这一次,有两个Y(),但一个比另一个“更接近”。所以比较近的优先。但是,如果您不使用new,编译器会给您一个警告。如果您确实使用了new,那么除了使编译时警告消失之外没有任何改变。故意弄两个Y()真是个坏主意。

如果基本类型(此处为IBase)由其他供应商/供应商编写,则可能会发生这种情况。示例:也许你在你的界面中引入了Y(),而此时基础没有那个功能。但随后IBase 的供应商正在发布他们产品的新版本,其中IBase 具有Y()。现在,如果您针对他们的新版本编译代码,“您的”Y() 仍将被调用,而不是他们的。但它会给出这个警告。如果包含new,警告就会消失。但最好是 (1) 如果您确定供应商的 Y() 可以完成这项工作,则完全删除您的 Y() 方法,或者 (2) 将您的 Y() 方法重命名为某个未使用的名称。

如果你想要的是多态性,当然使用abstract/virtual(仅限类成员)和override(在继承类中)。 override 不会引入任何新成员,只是现有(继承)成员的新实现。这只能对 非静态 成员(通常是方法或属性)进行。

【讨论】:

    【解决方案3】:

    对象的类型是 A,但 ref2 不访问在 B 类中定义的 Y() 版本,因为该方法是使用 new 修饰符而不是 override 修饰符声明的。因此,ref2 对象显示与 A 对象相同的描述。

    【讨论】:

    • new 修饰符实际上隐藏了基类的方法,但是如果对象的类型是 base 则它不会隐藏,您必须在 C# 中使用 virtual 和 override。希望这会有所帮助。
    • 任何对点“f对象的类型是基础的,那么它就不会隐藏”的引用
    • 这里是可能对您有用的链接:msdn.microsoft.com/en-us/library/ms173153.aspx
    猜你喜欢
    • 1970-01-01
    • 2022-06-16
    • 1970-01-01
    • 2012-08-26
    • 1970-01-01
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 2011-03-19
    相关资源
    最近更新 更多