我已经阅读了一些有关虚拟函数在c和c#之间的构造函数或析构函数中调用的不同行为的材料.然后,我测试以下代码,以确认c#可以调用虚拟派生的虚拟函数,因为其对象存在于构造函数之前.但是我发现结果与c中的类似代码相同.谁能告诉我为什么C#不能显示“ 22”而只能显示“ 12”的原因.
C#代码
public class Base
{
public Base() { fun(); }
public virtual void fun() { Console.WriteLine(1); }
}
public class Derived : Base
{
public Derived() { fun(); }
public virtual void fun() { Console.WriteLine(2); }
}
C代码
class Base
{
public:
Base(){fun();}
virtual void fun(){cout<<1;}
};
class Derived : Base
{
public:
Derived(){fun();}
virtual void fun(){cout<<2;}
};
c和C#的输出结果均为“ 12”.
解决方法:
您的C#代码中有错误.
要覆盖C#函数,您必须指定override关键字.
如果您再次编写virtual,则会使基本功能蒙上阴影,但是如果没有new关键字,您应该得到警告.
如果两个都声明为虚拟,则两个函数的名称相同!
让我们举个例子…
public class Base
{
public Base() { fun(); }
public virtual void fun() { Console.Write(1); }
}
public class Derived : Base
{
public Derived() { fun(); }
public override void fun() { Console.Write(2); }
}
public class DerivedWithError : Base
{
public DerivedWithError() { fun(); }
public new virtual void fun() { Console.Write(3); }
}
...
// This will print "22".
Base normal = new Derived();
Console.WriteLine();
// This will print "13" !!!
Base withError = new DerivedWithError ();
Console.WriteLine();
// Let's call the methods and see what happens!
// This will print "2"
normal.fun();
Console.WriteLine();
// This will print "1" !!!
withError.fun();
Console.WriteLine();
阴影表示“添加相同名称的新方法而不使用多态性”.
如果没有override关键字,您将禁用多态性.
因此,现在一切都应该干净并且易于理解.
DerivedWithError.fun()是一个全新的虚拟方法.它与基类中的fun函数具有相同的名称,但它们没有关联!
从虚拟表(或虚拟方法表,如果您更喜欢使用其他名称)的角度来讲,基本函数和派生函数的阴影会遮盖虚拟表中的两个不同条目,即使它们具有相同的名称也是如此.
如果改用override,则将强制派生类中的func方法覆盖Base.func占用的虚拟表项,这是多态性.
危险,但.NET允许,请务必谨慎使用语法!
您可以在C#中的构造函数中调用虚拟函数,但是通常在派生类中,如果在基本构造函数中调用了方法,则在使用派生类中的字段时应格外小心.
现在,为了简洁起见,避免混乱和风险,您应该避免在构造函数中调用虚拟方法,但实际上,多次使用是非常有用的.
例如,所谓的“工厂方法”不能访问任何变量.
public abstract class MyClass
{
public IList<string> MyList { get; private set; }
public MyClass()
{
this.MyList = this.CreateMyList();
}
protected abstract IList<string> CreateMyList();
}
public class MyDerived : MyClass
{
protected override IList<string> CreateMyList()
{
return new ObservableCollection<string>();
}
}
这是完全合法的,并且有效!