【问题标题】:new keyword in method signature方法签名中的新关键字
【发布时间】:2010-11-04 02:21:52
【问题描述】:

在执行重构时,我最终创建了一个如下例所示的方法。为简单起见,已更改数据类型。

我之前有一个这样的赋值语句:

MyObject myVar = new MyObject();

不小心被重构成了这样:

private static new MyObject CreateSomething()
{
  return new MyObject{"Something New"};
}

这是我的剪切/粘贴错误的结果,但 private static new 中的 new 关键字是有效的并且可以编译。

问题new 关键字在方法签名中的含义是什么?我认为这是 C# 3.0 中引入的东西?

这与override 有何不同?

【问题讨论】:

  • 关于方法隐藏的可取性的一些说明:blogs.msdn.com/ericlippert/archive/2008/05/21/…
  • @Eric.. 很棒的帖子。我从来没有想过以这种方式隐藏方法,因为是的,对象是一回事,但我们现在将它呈现为其他东西,所以我们想要我们呈现它的事物的行为。聪明...
  • 以后有一个重复的问题,我已经尝试过详细回答:Use new keyword in c#
  • 使用new 作为方法(或其他类型成员)的修饰符不是“C# 3.0 中引入的东西”。从 C# 的第一个版本开始,它就一直存在。
  • 在 SO 中这个问题有 4 个重复项。在我看来,这个会给你最好的理解:stackoverflow.com/questions/3117838/…。由@EricLippert 回答。

标签: c# methods keyword


【解决方案1】:

来自 MSDN 的新关键字参考:

MSDN Reference

以下是我在网上从 Microsoft MVP 中找到的一个非常有意义的示例: Link to Original

public class A
{
   public virtual void One();
   public void Two();
}

public class B : A
{
   public override void One();
   public new void Two();
}

B b = new B();
A a = b as A;

a.One(); // Calls implementation in B
a.Two(); // Calls implementation in A
b.One(); // Calls implementation in B
b.Two(); // Calls implementation in B

覆盖只能在非常特殊的情况下使用。来自 MSDN:

您不能覆盖非虚拟或 静态方法。被覆盖的基础 方法必须是虚拟的、抽象的或 覆盖。

因此需要“new”关键字来允许您“覆盖”非虚拟和静态方法。

【讨论】:

  • 我只是运行您的示例.. 即使您没有指定“新”,它也会覆盖,对吧?
  • @MichaelSync 正是如此,为什么我们需要提及新关键字?
  • "虽然您可以在不使用 new 修饰符的情况下隐藏成员,但您会收到编译器警告。"根据链接的文档。因此,关键字表明您有意隐藏,并且不会发出警告。
  • -1 -> 根本不需要 - 行为将是相同的。新手很困惑new 是关于什么的,但我认为它实际上只是为了便于阅读——编译器只是警告你,当你调用与基类同名的派生类方法时,你不会得到基类的方法你可能会想......这很奇怪......纯粹是为了可读性?
  • 为了完整起见:如果 A 和 B 类都实现了接口IMyInterface,则将调用 Derived 类的实现。因此IMyInterface c = new B() 将调用B 类的实现。如果只有一个类实现了接口,则调用实现它的类中的方法。
【解决方案2】:

不,它实际上不是“新”(请原谅双关语)。它基本上用于“隐藏”一种方法。即:

public class Base
{
   public virtual void Method(){}
}

public class Derived : Base
{
   public new void Method(){}
}

如果你这样做:

Base b = new Derived();
b.Method();

Base 中的方法将被调用,而不是派生中的方法。

更多信息:http://www.akadia.com/services/dotnet_polymorphism.html

重新编辑:在我给出的示例中,如果您要“覆盖”而不是使用“new”,那么当您调用 b.Method();由于多态性,会调用派生类的方法。

【讨论】:

  • 公共类 Base { public virtual void Method() { Console.WriteLine("Base"); } } 公共类 Derived : Base { public void Method() { Console.WriteLine("Derived"); } } 基 b = new Derived(); b.方法();我得到了“Base”..如果我在 Derived 类中添加“new”,我仍然得到“Base”..
  • @michael 方法仍然是虚拟的
  • @MichaelSync:您应该会看到带有该代码的警告;警告 Derived.Method()' 隐藏了继承的成员 'Base.Method()'。要使当前成员覆盖该实现,请添加 override 关键字。否则添加新关键字。
  • @MichaelSync 如果省略“覆盖”一词,则默认行为是“新”,例如方法隐藏。因此,您将“新”一词排除在外这一事实没有任何区别。
  • 是的。我也是这么想的。不知道为什么 C# 添加了“new”关键字..只是为了让警告消失..stackoverflow.com/questions/8502661/…
【解决方案3】:

正如其他人解释的那样,它用于隐藏现有方法。它对于覆盖父类中的非虚拟方法很有用。

请记住,创建“新”成员不是多态的。如果将对象强制转换为基类型,它将不会使用派生类型的成员。

如果你有一个基类:

public class BaseClass
{
    public void DoSomething() { }
}

然后是派生类:

public class DerivedType : BaseClass
{
    public new void DoSomething() {}

}

如果你声明一个DerivedType的类型然后强制转换它,DoSomething()方法不是多态的,它会调用基类的方法,而不是派生的方法。

BaseClass t = new DerivedType();
t.DoSomething();// Calls the "DoSomething()" method of the base class.

【讨论】:

  • 即使你从 DerievedType 中删除“new”,它也会从基类调用方法。
  • 我认为您的第二段将整个主题都钉在了一起。在处理来自基类引用的调用时,“new”不是多态的......即。当您拨打电话时,您会得到指定的内容。 “覆盖”是多态的......即。你会得到 类层次结构 指定的内容。
  • 这个东西怎么定义得这么模糊?对于一个新手来说非常令人沮丧。不,它与多态性无关——它与在方法签名中没有override 具有完全相同的行为
  • 隐藏了什么? new 肯定不可能从派生类型中隐藏基类型,因为您不能总是使用base.<type> 表示法访问基类型吗?
  • @thatWiseGuy 它是“隐藏的”,因为在代码中使用派生类时不会调用基方法。它仍然可以使用base.<method> 在内部调用,如果您在代码中强制转换为基本类型,也可以在外部调用它。从技术上讲,将其视为“覆盖”基本方法比“隐藏”它更准确。
【解决方案4】:

来自文档:

如果派生类中的方法以new关键字开头,则该方法被定义为独立于基类中的方法。

这在实践中意味着什么:

如果您从另一个类继承并且您有一个共享相同签名的方法,您可以将其定义为“新”,以便它独立于父类。这意味着,如果您有对“父”类的引用,则将执行该实现,如果您有对子类的引用,则将执行该实现。

就我个人而言,我尽量避免使用“new”关键字,因为它通常意味着我的类层次结构错误,但有时它会很有用。一个地方是版本控制和向后兼容性。

the MSDN for this里有很多信息。

【讨论】:

  • 发生这种行为的事实与new 的存在无关。它是语法/可读性。
【解决方案5】:

长话短说——这不是必需的,它不会改变任何行为,而且纯粹是为了可读性。

这就是为什么在 VS 中你会看到一些波浪状的东西,但你的代码会编译并运行得非常好,并且符合预期。

人们不得不怀疑是否真的值得创建 new 关键字,而这意味着开发人员承认“是的,我知道我隐藏了一个基本方法,是的,我知道我没有做任何与virtualoverriden(多态性)——我真的很想创建它自己的方法”。

这对我来说有点奇怪,但可能只是因为我来自 Java 背景,C# 继承和 Java 之间存在根本区别:在 Java 中,方法默认是虚拟的,除非由final。在C# 中,方法默认是最终的/具体的,除非virtual 指定。

【讨论】:

    【解决方案6】:

    表示方法替换了基类继承的同名方法。在您的情况下,您可能在基类中没有该名称的方法,这意味着 new 关键字是完全多余的。

    【讨论】:

      【解决方案7】:

      来自MSDN

      使用 new 修饰符显式隐藏继承自 基类。要隐藏继承的成员,请在派生中声明它 使用相同名称的类,并使用 new 修饰符对其进行修改。

      【讨论】:

        【解决方案8】:

        小心这个问题。
        您有一个在基类中实现的接口中定义的方法。然后创建一个隐藏接口方法的派生类,但不要专门声明派生类实现接口。如果您随后通过对接口的引用调用该方法,则将调用基类的方法。 但是,如果您的派生类确实实现了该接口,那么无论使用哪种类型的引用都会调用其方法。

        interface IMethodToHide
        {
            string MethodToHide();
        }
        
        class BaseWithMethodToHide : IMethodToHide
        {
            public string MethodToHide()
            {
                return "BaseWithMethodToHide";
            }
        }
        
        class DerivedNotImplementingInterface   : BaseWithMethodToHide
        {
            new public string MethodToHide()
            {
                return "DerivedNotImplementingInterface";
            }
        }
        
        class DerivedImplementingInterface : BaseWithMethodToHide, IMethodToHide
        {
            new public string MethodToHide()
            {
                return "DerivedImplementingInterface";
            }
        }
        
        class Program
        {
            static void Main()
            {
                var oNoI = new DerivedNotImplementingInterface();
                IMethodToHide ioNoI = new DerivedNotImplementingInterface();
        
                Console.WriteLine("reference to the object type DerivedNotImplementingInterface calls the method in the class " 
                    + oNoI.MethodToHide());
                // calls DerivedNotImplementingInterface.MethodToHide()
                Console.WriteLine("reference to a DerivedNotImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
                    + ioNoI.MethodToHide());
                // calls BaseWithMethodToHide.MethodToHide()
                Console.ReadLine();
        
                var oI = new DerivedImplementingInterface();
                IMethodToHide ioI = new DerivedImplementingInterface();
        
                Console.WriteLine("reference to the object type DerivedImplementingInterface calls the method in the class " 
                    + oI.MethodToHide());
                // calls DerivedImplementingInterface.MethodToHide()
                Console.WriteLine("reference to a DerivedImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
                    + ioI.MethodToHide());
                // calls DerivedImplementingInterface.MethodToHide()
                Console.ReadLine();
        
            }
        }
        

        【讨论】:

          【解决方案9】:

          FWIW,我已经编写 C# 代码很长一段时间了,我需要 new 关键字的唯一情况是以下 OO 层次结构场景:

          public abstract class Animal
          {
              protected Animal(Animal mother, Animal father)
              {
                  Mother = mother;
                  Father = father;
              }
          
              public Animal Mother { get; }
              public Animal Father { get; }
          }
          
          public class Dog : Animal
          {
              public Dog(Dog mother, Dog father)
                  : base(mother, father)
              {
              }
          
              public new Dog Mother => (Dog)base.Mother;
              public new Dog Father => (Dog)base.Father;
          }
          

          几点说明:

          • 没有违反 OO 规则,基类仍然可以正常工作。
          • 属性不是故意标记为virtual,它们不需要。
          • 事实上,由于有了新的关键字,我们执行了更多的 OO 规则:狗不能有任何动物作为父亲/母亲。它必须是一只狗。
          • 使用new 关键字是因为我们不能仅通过更改其返回类型来“覆盖”属性(或方法)。 new 是拥有良好 OO 层次结构的唯一方法。

          【讨论】:

            猜你喜欢
            • 2011-11-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-27
            • 2018-03-06
            • 2010-11-06
            相关资源
            最近更新 更多