C#中“ new”修饰符在隐藏方面有哪些实际用途?


21

我和一位同事正在研究newC#中关键字的行为,因为它适用于隐藏的概念。从文档中

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

我们已经阅读了文档,并且了解了它的基本功能以及工作方式。我们真正无法解决的是为什么您首先需要这样做。自2003年以来,该修饰符就已经存在了,而我们俩一直都在使用.Net进行更长时间的工作,而且从未出现过。

在实际意义上何时需要采取这种行为(例如:应用于业务案例)?这是功能已经过时了吗?或者它在我们所做的事情中(只是我们在处理Web窗体和MVC应用程序以及一些小因素WinForms和WPF)干脆不常见?在尝试并使用该关键字时,我们发现了一些行为,如果使用不当,可能会带来一些危险。

这听起来有些开放,但是我们正在寻找一种特定的用例,该用例可以应用于发现该特定工具有用的业务应用程序。


9
也许您也已经读过它,但是Eric Lippert(C#编译器开发人员)有一篇有趣的文章,介绍了为什么将方法隐藏添加到C#:blogs.msdn.com/b/ericlippert/archive/2008/05/21/…。这可以回答您的部分问题,但是我没有适合您的业务案例,因此我将其放在评论中。
2012年

3
:@Jalayn:商业案例是由Eric在这篇文章中讨论blogs.msdn.com/b/ericlippert/archive/2004/01/07/...
布赖恩

Answers:


22

您可以使用它来模仿返回类型的协方差。 埃里克·利珀特(Eric Lippert)的解释。Eric提供了以下示例代码:

abstract class Enclosure
{
    protected abstract Animal GetContents();
    public Animal Contents() { return this.GetContents(); }
}
class Aquarium : Enclosure
{
    public new Fish Contents() { ... }
    protected override Animal GetContents() { return this.Contents(); }
}

这是一种解决方法。 public override Fish Contents() { ... }尽管安全,但仍不合法。

一般情况下,你应该使用这种方法隐藏,因为它混淆消费者类的(上述具体的例子不存在这个问题)。如果您不想覆盖现有方法,只需为新方法命名。

您可能需要方法隐藏的现实世界情况是,如果基类的提供者添加了您已经添加到派生类的泛型方法。这样的程序将在不使用new关键字的情况下进行编译(并发出警告),但是补充new说:“我知道该方法的版本正在替换基类的版本。这太可怕且令人困惑,但是我们坚持使用它。” 这仍然比强制派生类重命名其方法要好。

仅允许将派生的方法视为替代将导致问题。忽略与实现编译器有关的任何问题,新方法在语义上与基本方法不同,但是当要求使用相同名称的方法调用时,多态会导致新方法被调用。

埃里克·利珀特(Eric Lippert)在这篇文章中详细讨论了这种情况。


1
+1表示new“这太可怕了,令人困惑”。
Avner Shahar-Kashtan

我认为Eric的示例非常有帮助,仅是因为我处于类似情况下...我有一个实现非平凡方法的基类,该方法返回TBase,而派生类应返回TDerived。在这种情况下,感觉就像是必要的邪恶。
凯尔·巴兰

4

我认为它是存在的,以防您可能需要它来执行语言设计师可能没有想到的事情。C#在许多方面都是对Java早期版本的反应。Java所做的一件事是非常明确地发现开发者的问题,以消除开发者自以为是的想法。C#采取了略有不同的方法,并为开发人员提供了更多功能,使他们有更多机会发掘自己。unsafe关键字就是一个例子。此new关键字是另一个。

现在,它可能没有它有用,unsafe但是一旦您进入语言规范,就很难摆脱语言规范。


5
Java没有新功能,因为Java将所有方法都视为虚拟方法。根据埃里克·利珀特(Eric Lippert)的说法,支持新产品的动机是解决脆性的基层问题。新的存在对于现实世界的业务使用是必需的,而不仅仅是低级库的使用。Java没有新的(并且没有非虚拟方法)意味着如果基类引入了派生类中已在使用的新方法,则尽管应允许基类的开发者,但现有代码可能会中断忽略使用它的代码。
布赖恩

3

它告诉读者,“我故意隐藏了该方法的基类的实现”,而不是偶然地。


5
这使我震惊,乞求这个问题。OP知道自己在做什么,但想知道为什么。
布赖恩

0

您可能想通过其他名称使先前的成员可用:

class VehicleClass
{
  public int AnyProperty
  {
    get; set;
  }

  public int AnyFunction() { return 0; }
} // VehicleClass

class IntermediateClass : VehicleClass
{
  public int PreviousAnyProperty
  {
    get { return AnyProperty; }
    set { AnyProperty = value  }
  }

  public int PreviousAnyFunction() { return AnyFunction(); }
} // IntermediateClass 

class CarClass : IntermediateClass
{
  public new int AnyProperty
  {
    get ; set ;
  }

  public new int AnyFunction() { return 5; }
} // class CarClass

class ExampleClass
{

  public static void Main()
  {
    using (CarClass MyCar = new CarClass())
    {
      int AnyInt1 = MyCar.PreviousAnyProperty;
      MyCar.PreviousAnyProperty = 7;

      int AnyInt2 = MyCar.PreviousAnyFunction();

      int AnyInt3 = MyCar.AnyProperty;
      MyCar.AnyProperty = 45;

      int AnyInt4 = MyCar.AnyFunction();
    }
  } // static void Main()

} // class CarClass

干杯。


我熟悉它的工作原理,但我的问题确实是,您为什么要通过另一个名称来提供以前的成员?这破坏了所有种类的合同,使某些通用部分无用。
乔尔·埃瑟顿

@Joel Etherton:您可能已经知道,有时开发人员必须“挑选”其他人的编程代码。而且我们可能无权修改,也可能扩展类。并且可能需要同时使用以前的成员和新成员。
umlcat 2012年

0

在为遗留代码创建测试套件时,我发现它非常有用。它使我能够隐藏外部依赖关系,例如在其他方法使用的方法中查询数据库。为了能够测试其他方法,我可以隐藏依赖于外部资源的原始逻辑,而无需在测试类中向这些方法添加virtual关键字。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.