允许在虚拟方法上使用new的c的用例是什么?


What is the use case for C# allowing to use new on a virtual method?

主要是Java开发人员,当我有一天无意中使用新关键字而不是重写时,我对结果感到有点惊讶。

似乎new关键字删除了继承树中该级别方法的"virtuality",因此对向下转换到父类的子类实例调用方法不会解析为子类中的方法实现。

这种行为的实际用例是什么?

澄清:我理解当父母不是虚拟的时候使用new。我更好奇为什么编译器允许新的和虚拟的结合。

以下示例说明了不同之处:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;

public class FooBar
{
    public virtual void AAA()
    {
        Console.WriteLine("FooBar:AAA");
    }

    public virtual void CCC()
    {
        Console.WriteLine("FooBar:CCC");
    }
}

public class Bar : FooBar
{
    public new void AAA()
    {
        Console.WriteLine("Bar:AAA");
    }

    public override void CCC()
    {
        Console.WriteLine("Bar:CCC");
    }
}

public class TestClass
{
    public static void Main()
    {
        FooBar a = new Bar();
        Bar b = new Bar();
        Console.WriteLine("Calling FooBar:AAA");
        a.AAA();
        Console.WriteLine("Calling FooBar:CCC");
        a.CCC();
        Console.WriteLine("Calling Bar:AAA");
        b.AAA();
        Console.WriteLine("Calling Bar:CCC");
        b.CCC();
        Console.ReadLine();
    }
}

这将产生以下输出:

1
2
3
4
5
6
7
8
Calling FooBar:AAA
FooBar:AAA
Calling FooBar:CCC
Bar:CCC
Calling Bar:AAA
Bar:AAA
Calling Bar:CCC
Bar:CCC

用例:

  • 今天,您使用第三方库并从类Fruit派生类Banana
  • Banana中实现一个名为Peel的方法。在Fruit中没有Peel
  • 明天,第三方将发布新版本的库,包括一个虚拟的Fruit.Peel方法
  • 明天重新编译代码。是否要覆盖Fruit.Peel?很可能不是-它可能有一个完全不同的含义。相反,您用Banana.Peel隐藏它,所有现有的代码都像今天一样工作。

换句话说,它主要是为了避免版本控制问题。在爪哇,即使你不想这样做,你也会重写EDOCX1 6,很可能导致难以诊断的错误。


从个人经验来看,我主要看到"new"关键字在原始父方法未被指定为虚拟的情况下使用,但需要重写行为。应用"new"关键字"隐藏"父方法。而且,正如您在代码示例中所观察到的,用"new"编写的方法只在直接使用该类型时执行。如果使用父类型,将调用父原始方法。

为了更直接地回答您的问题,它提供了一种方法,当父方法未被标记为虚方法时,可以重写方法。

编辑:作为一个旁白,添加"new"关键字来隐藏一个自然不可重写的父方法实际上不会改变生成的IL中的任何内容。但它是一种向开发人员显式声明"嘿,你在这里隐藏了一个父方法,而不是重写它"


假设地……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class BaseCollection<T>
{
  // void return - doesn't seem to care about notifying the
  // client where the item was added; it has an IndexOf method
  // the caller can use if wants that information
  public virtual void Add(T item)
  {
    // adds the item somewhere, doesn't say where
  }

  public int IndexOf(T item)
  {
     // tells where the item is
  }
}

public class List<T> : BaseCollection<T>
{
  // here we have an Int32 return because our List is friendly
  // and will tell the caller where the item was added
  new public virtual int Add(T item) // <-- clearly not an override
  {
     base.Add(item);
     return base.IndexOf(item);
  }
}

这里我使用"new"修饰符,因为列表引用将从basecollection中隐藏add方法。默认情况下,从基中隐藏成员会从编译器中生成警告(如果编译设置为在警告时失败,则为错误)。所以我基本上是告诉编译器…"是的,我知道我用void返回隐藏了addmethod,这是所需的功能—只需使用它即可。"