我有 Java 知识,最近几天一直在学习 C#。现在我遇到了“virtual”关键字,正如此链接所建议的那样,它用于允许在子类中覆盖相应的方法、属性等。现在我认为即使不使用“virtual”关键字,我们也可以覆盖方法。那为什么有必要呢?
5 回答
virtual
如果您真的想要override
子类中的方法,则需要关键字。否则,基本实现将被新实现隐藏,就像您使用new
关键字声明它一样。
通过“覆盖”它们而不声明基方法来隐藏方法virtual
会使您没有多态性,这意味着:如果您将专用版本“转换”为“基”版本并调用方法,则始终将使用基类实现代替被覆盖的版本 - 这不是你所期望的。
例子:
class A
{
public void Show() { Console.WriteLine("A"); }
}
class B : A
{
public void Show() { Console.WriteLine("B"); }
}
A a = new A();
B b = new B();
a.Show(); // "A"
b.Show(); // "B"
A a1 = b;
a1.Show(); // "A"!!!
virtual
是一种定义方法具有默认实现的方式,但该实现可以在子类中被覆盖。除了使用 virtual 之外,您不能在不使用new
关键字的情况下直接覆盖方法(这通常是不好的做法)。
实现的一个很好的例子virtual
是ToString()
方法。C# 中的每个对象都保证能够调用ToString()
,因为每个对象都继承自System.Object
包含虚方法的基类ToString()
。然而,派生类可以覆盖它,并提供它们自己的实现,这可能对对象的用户更有用。
更新:我最近写了一篇博文,深入探讨了这个话题。在这里查看。
现在我认为即使不使用“virtual”关键字,我们也可以覆盖方法。
不,你不能。与 Java 不同,在 C# 中成员默认是密封的,virtual
除非您使用关键字标记它们,否则您无法覆盖它们。
看看这个例子:
void Main()
{
var o1 = new S();
Console.WriteLine(((B)o1).m1());
}
public class B
{
public virtual string m1() {
return "m1";
}
}
public class S : B
{
override public string m1() {
return "overridden m1";
}
}
在此示例中,子类S
被实例化并分配给对象变量o1
。在Console.WriteLine
语句的参数中,它被强制转换为基类B
,然后m1
调用方法。因为我们virtual
在基类B
和override
子类中使用过S
,所以我们得到
被覆盖的 m1
作为输出。如果您在in和子类virtual
的方法声明中删除,那么您将得到m1
B
override
S
米1
作为输出,这意味着强制转换也具有使用m1
基类中方法的原始声明的效果B
。
注意如果您new
在 subclass 中使用关键字S
,例如
new public string m1() {
return "overridden m1";
}
假设virtual
基类中的关键字B
不存在,您将获得输出
米1
也是。如果您不将其转换为B
,则将使用新方法。这称为遮蔽(或隐藏)方法(基类的原始方法)。
概括:
要覆盖一个方法,如果您强制转换为基类也应该有效,请在基类和子类中使用
virtual
关键字。override
如果您打算覆盖仅应在子类中处于活动状态的方法,请
new
在子类方法声明中使用关键字。正如您所看到的,没有它它也可以工作,但如果它在那里更好,所以每个人都知道这是该方法的新版本。
如果派生类被实例化为基类,virtual / override 将允许派生类覆盖基函数。例子:
class A
{
public virtual void Show() { Console.WriteLine("A"); }
}
class B : A
{
public override void Show() { Console.WriteLine("B"); }
}
A a = new A();
B b = new B();
a.Show();
b.Show();
A a1 = b;
a1.Show(); // here is the test!!!
B b1 = new B();
b.Show();
With virtual / override :
A
B
B
B
After removing virtual / override keywords (or simply removing override):
A
B
A
B