我有 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的方法声明中删除,那么您将得到m1BoverrideS
米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