C#-关键字用法虚拟+;覆盖与新建

C#-关键字用法虚拟+;覆盖与新建,c#,syntax,overriding,method-hiding,member-hiding,C#,Syntax,Overriding,Method Hiding,Member Hiding,在基类型“virtual”中声明方法,然后使用“override”关键字在子类型中重写方法,与在子类型中声明匹配方法时仅使用“new”关键字有什么区别 关键字“new”不重写,它表示与基类方法无关的新方法 public class Foo { public bool DoSomething() { return false; } } public class Bar : Foo { public new bool DoSomething() { return true; }

在基类型“
virtual
”中声明方法,然后使用“
override
”关键字在子类型中重写方法,与在子类型中声明匹配方法时仅使用“
new
”关键字有什么区别

关键字“new”不重写,它表示与基类方法无关的新方法

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}
此选项打印为false,如果使用override,则会打印为true。

(基本代码取自Joseph Daigle)


因此,如果您正在执行真正的多态性,则应始终覆盖。唯一需要使用“new”的地方是当该方法与基类版本没有任何关系时。

关键字
new
实际上创建了一个仅存在于该特定类型上的全新成员

比如说

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

这两种类型上都存在该方法。当您使用反射并获取类型为
Bar
的成员时,实际上会发现两个名为
DoSomething()
的方法看起来完全相同。通过使用
new
可以有效地将实现隐藏在基类中,这样当类派生自
Bar
(在我的示例中)时,对
base.DoSomething()的方法调用
转到
Bar
而不是
Foo

override关键字和new关键字之间的区别在于前者执行方法重写,后者执行方法隐藏

查看以下链接了解更多信息


以下是一些代码,用于理解虚拟方法和非虚拟方法行为的差异:

class A
{
    public void foo()
    {
        Console.WriteLine("A::foo()");
    }
    public virtual void bar()
    {
        Console.WriteLine("A::bar()");
    }
}

class B : A
{
    public new void foo()
    {
        Console.WriteLine("B::foo()");
    }
    public override void bar()
    {
        Console.WriteLine("B::bar()");
    }
}

class Program
{
    static int Main(string[] args)
    {
        B b = new B();
        A a = b;
        a.foo(); // Prints A::foo
        b.foo(); // Prints B::foo
        a.bar(); // Prints B::bar
        b.bar(); // Prints B::bar
        return 0;
    }
}

除了技术细节之外,我认为使用virtual/override在设计上传达了很多语义信息。当您声明一个虚拟方法时,您指出您期望实现类可能希望提供它们自己的非默认实现。同样地,在基类中省略这一点,会声明默认方法应该满足所有实现类的要求。类似地,可以使用抽象声明强制实现类提供自己的实现。同样,我认为这传达了程序员对代码使用的期望。如果我同时编写基类和实现类,并且发现自己在使用new,我会认真重新考虑不在父类中使方法虚拟的决定,并明确声明我的意图。

virtual/override告诉编译器这两个方法是相关的,并且在某些情况下,当您认为在调用第一个(虚拟)方法时,实际上调用第二个(重写)方法是正确的。这是多态性的基础。

(new SubClass() as BaseClass).VirtualFoo()
将调用子类的重写VirtualFoo()方法

new告诉编译器您正在向派生类添加一个与基类中的方法同名的方法,但它们之间没有关系

(new SubClass() as BaseClass).NewBar()
将调用基类的NewBar()方法,而:

(new SubClass()).NewBar()

将调用子类的NewBar()方法。

我总是发现这样的事情更容易通过图片理解:

再次以joseph daigle的密码为例

public class Foo
{
     public /*virtual*/ bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public /*override or new*/ bool DoSomething() { return true; }
}
如果您随后像这样调用代码:

Foo a = new Bar();
a.DoSomething();
注意:重要的是,我们的对象实际上是一个
,但我们将存储在
Foo
类型的变量中(这类似于强制转换)

然后,结果如下所示,这取决于您在声明类时是使用了
virtual
/
override
还是
new

  • new
    关键字用于隐藏。-意味着您正在运行时隐藏您的方法。输出将基于基类方法
  • 覆盖
    用于覆盖。-表示您正在使用基类的引用调用派生类方法。输出将基于派生类方法

我的解释来自于使用属性来帮助理解差异

覆盖
很简单,对吗?基础类型覆盖父类型的

new
可能是误导(对我来说是)。使用属性更容易理解:

public class Foo
{
    public bool GetSomething => false;
}

public class Bar : Foo
{
    public new bool GetSomething => true;
}

public static void Main(string[] args)
{
    Foo foo = new Bar();
    Console.WriteLine(foo.GetSomething);

    Bar bar = new Bar();
    Console.WriteLine(bar.GetSomething);
}
使用调试器,您可以注意到
Foo-Foo
2
GetSomething
属性,因为它实际上有两个版本的属性,
Foo
Bar
,要知道使用哪一个,c#选择当前类型的属性

如果您想使用该条的版本,您应该使用override或使用
Foo-Foo


Bar
只有1,因为它希望
GetSomething

有全新的行为,而不是用任何方式标记方法:使用对象的编译类型绑定此方法,而不是运行时类型(静态绑定)

virtual
标记方法意味着:使用对象的运行时类型而不是编译时类型(动态绑定)绑定此方法

在派生类中将基类
virtual
方法标记为
override
意味着:这是要使用对象的运行时类型(动态绑定)绑定的方法

在派生类中将基类
virtual
方法标记为
new
意味着:这是一个新方法,与基类中同名的方法没有关系,应该使用对象的编译时类型(静态绑定)对其进行绑定

未在派生类中标记基类
虚拟
方法意味着:此方法标记为
(静态绑定)

标记一个方法
abstract
意味着:这个方法是虚拟的,但我不会为它声明一个主体,它的类也是抽象的(动态绑定)

你应该