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
有2GetSomething
属性,因为它实际上有两个版本的属性,Foo
和Bar
,要知道使用哪一个,c#选择当前类型的属性
如果您想使用该条的版本,您应该使用override或使用Foo-Foo
Bar
只有1,因为它希望GetSomething
有全新的行为,而不是用任何方式标记方法:使用对象的编译类型绑定此方法,而不是运行时类型(静态绑定)
用virtual
标记方法意味着:使用对象的运行时类型而不是编译时类型(动态绑定)绑定此方法
在派生类中将基类virtual
方法标记为override
意味着:这是要使用对象的运行时类型(动态绑定)绑定的方法
在派生类中将基类virtual
方法标记为new
意味着:这是一个新方法,与基类中同名的方法没有关系,应该使用对象的编译时类型(静态绑定)对其进行绑定
未在派生类中标记基类虚拟
方法意味着:此方法标记为新
(静态绑定)
标记一个方法abstract
意味着:这个方法是虚拟的,但我不会为它声明一个主体,它的类也是抽象的(动态绑定) 你应该