• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

C# 构造函数如何调用虚方法

c# 搞代码 4年前 (2022-01-09) 20次浏览 已收录 0个评论

谜题

在C#中,用virtual关键字修饰的方法(属性、事件)称为虚方法(属性、事件),表示该方法可以由派生类重写(override)。虚方法是.NET中的重要概念,可以说在某种程度上,虚方法使得多态成为可能。

然而虚方法的使用却存在着很大学问,如果滥用的话势必对程序产生很大的负面影响。比如下面这个例子:

public class Puzzle
{
  public Puzzle()
  {
    Name = "Virtual member call in constructor";
    Solve();
  }

  public virtual string Name { get; set; }

  public virtual void Solve()
  {
  }
}

如果您的Visual Studio没有安装ReSharper,那么上面的代码不会有任何异常。但如果安装了,在构造函数内部给Name赋值和调用Solve时就会在下面产生一个波浪线,即警告:virtual member call in constructor。

这是什么原因呢?我们在构造函数中调用虚方法,碍着ReSharper什么事儿了?

其实这个警告就是提醒我们不要在非封闭类型的构造函数内调用虚方法或虚属性。但为什么这样做不合适呢?在解惑之前,我们先来了解两个概念。

类型的初始化顺序

我们先来看这样一段代码:

class Base
{
  public Base()
  {
    Console.WriteLine("Base constructor");
  }
}
class Derived : Base
{
  public Derived()
  {
    Console.WriteLine("Derived constructor");
  }
}
static class Program
{
  static void Main()
  {
    new Derived();
    Console.Read();
  }
}

猜一猜它的输出结果是什么?

你也许已经猜到了,它的结果是:

Base constructor
Derived constructor

我们在初始化一个对象时,总是会先执行基类的构造函数,然后再执行子类的构造函数。

虚方法调用

我们再来看一段代码:

cla<p style="color:transparent">本文来源gao!%daima.com搞$代*!码$网3</p>ss Base
{
  public void M()
  {
    Console.WriteLine("Base.M");
  }

  public virtual void V()
  {
    Console.WriteLine("Base.V");
  }
}
class Derived : Base
{
  public new void M()
  {
    Console.WriteLine("Derived.M");
  }

  public override void V()
  {
    Console.WriteLine("Derived.V");
  }
}
static class Program
{
  static void Main()
  {
    var d = new Derived();
    Base b = d;
    b.M();
    b.V();
    d.M();
    d.V();
    Console.Read();
  }
}

再来猜一猜输出结果吧。

貌似应该是:

Base.M
Base.V
Derived.M
Derived.V

但运行一下会发现,真正的结果是这样的:

Base.M
Derived.V
Derived.M
Derived.V

这是为什么呢?

原来对于非虚方法调用,编译器会进行一些额外的“动作”。比如找出所调用对象的实际类型,以访问正确的方法表(调用b.V()的时候就会找到变量b的实际类型Derived,从而输出Derived.V)。

解惑

现在回到我们最初的谜题,virtual member call in constructor。结合以上两个知识点,会有哪些发现?

我们稍微改造一下虚方法调用的那个例子。

class Foo
{
  public Foo(string s)
  {
    Console.WriteLine(s);
  }
  public void Bar() { }
}

class Base
{
  public Base()
  {
    V(); // Virtual member call in constructor
  }
  public virtual void V()
  {
    Console.WriteLine("Base.V");
  }
}
class Derived : Base
{
  private Foo foo;
  public Derived()
  {
    foo = new Foo("foo in Derived");
  }

  public override void V()
  {
    Console.WriteLine("Derived.V");
    foo.Bar(); // will throw NullReferenceException
  }
}

搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:C# 构造函数如何调用虚方法

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址