我已经阅读了一些关于在c ++和c#之间构造函数或析构函数中调用的虚函数的不同行为的资料。我测试下面的代码,以确认c#可以调用虚拟dirived虚函数,因为它的对象存在于构造函数之前。但是我发现结果与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”。
答案 0 :(得分:2)
您的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()是一个全新的虚方法。它与基类中的函数名称相同,但它们不相关!
从虚拟表(或虚拟方法表,如果您更喜欢其他名称)的角度来讲,通过遮蔽基本函数和派生函数占用虚拟表中的两个不同条目,如果它们具有相同的名称。
如果改为使用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>();
}
}
这是完全合法的,它有效!
答案 1 :(得分:1)
问题在于,即使函数具有相同的名称“fun()
”,每个函数都是声明它的类的成员。因此,当您从基类ctor调用fun()
时,您正在调用Base.func()
,当您从派生类调用fun()
时,它就像Derived.fun()
;如果您想输出“22”,则需要覆盖fun()
类中的Derived
。
public class Derived : Base
{
public Derived() { fun(); }
public override void fun() { Console.WriteLine(2); }
}
答案 2 :(得分:0)
我假设您在每种情况下都在创建派生实例。
您的部分问题是建议不要在构造函数中调用虚方法。
fun
点
因此,当您致电base::fun
fun()
fun
指向derived::fun
。下次拨打fun()
时,它会打印2 MSDN强烈建议不要这样做:Do not call overridable methods in constructors
有效的C ++提出了更强有力的建议:Never Call Virtual Functions during Construction or Destruction
这是一个类似的question