为属性赋值时在构造函数中调用虚拟成员

发布于 2024-10-11 11:18:47 字数 2617 浏览 5 评论 0原文

我有一个抽象类和一个派生类。抽象类定义了一个名为 Message 的抽象属性。在派生类中,属性是通过重写抽象属性来实现的。派生类的构造函数采用字符串参数并将其分配给其 Message 属性。在 Resharper 中,此赋值会导致警告“构造函数中的虚拟成员调用”。

AbstractClass 有这样的定义:

public abstract class AbstractClass {
    public abstract string Message { get; set; }

    protected AbstractClass() {}

    public abstract void PrintMessage();
}

DerivedClass 如下:

using System;

public class DerivedClass : AbstractClass {
    private string _message;

    public override string Message {
        get { return _message; }
        set { _message = value; }
    }

    public DerivedClass(string message) {
        Message = message; // Warning: Virtual member call in a constructor
    }

    public DerivedClass() : this("Default DerivedClass message") {}

    public override void PrintMessage() {
        Console.WriteLine("DerivedClass PrintMessage(): " + Message);
    }
}

我确实发现了有关此警告的其他一些问题,但在这些情况下,存在对方法的实际调用。例如,在 这个问题中,Matt Howels 的答案包含一些示例代码。我这里再重复一下,方便参考。

class Parent {
    public Parent() {
        DoSomething();
    }
    protected virtual void DoSomething() {};
}

class Child : Parent {
    private string foo;
    public Child() { foo = "HELLO"; }
    protected override void DoSomething() {
        Console.WriteLine(foo.ToLower());
    }
}

马特没有描述警告会出现什么错误,但我假设它将出现在父构造函数中调用 DoSomething 时。在这个例子中,我明白了虚拟成员被调用是什么意思。成员调用发生在基类中,基类中只存在虚方法。

然而,在我的情况下,我不明白为什么为 Message 赋值会调用虚拟成员。 Message 属性的调用和实现都在派生类中定义。

虽然我可以通过密封派生类来消除错误,但我想了解为什么这种情况会导致警告。

更新 根据 Brett 的回答,我尽力创建一个从 DerivedClass 派生的 ChildClass,最终会导致异常。这就是我的想法:

using System;

public class ChildClass : DerivedClass {
    private readonly string _foo;

    public ChildClass() : base("Default ChildClass Message") {
        _foo = "ChildClass foo";
    }

    public override string Message {
        get { return base.Message; }
        set {
            base.Message = value;
            Console.WriteLine(_foo.ToUpper() + " received " + value);
        }
    }
}

当然,在 Message setter 中使用 _foo 有点愚蠢,但重点是 ReSharper 没有发现这个类有任何问题。

但是,如果您尝试在如下程序中使用 ChildClass:

internal class Program {
    private static void Main() {
        var childClass = new ChildClass();
        childClass.PrintMessage();
    }
}

创建 ChildClass 对象时,您将收到 NullReferenceException。由于 _foo 尚未初始化,ChildClass 尝试使用 _foo.ToUpper() 将引发异常。

I have an Abstract class and a Derived class. The abstract class defines an abstract property named Message. In the derived class, the property is implemented by overriding the abstract property. The constructor of the derived class takes a string argument and assigns it to its Message property. In Resharper, this assignment leads to a warning "Virtual member call in constructor".

The AbstractClass has this definition:

public abstract class AbstractClass {
    public abstract string Message { get; set; }

    protected AbstractClass() {}

    public abstract void PrintMessage();
}

And the DerivedClass is as follows:

using System;

public class DerivedClass : AbstractClass {
    private string _message;

    public override string Message {
        get { return _message; }
        set { _message = value; }
    }

    public DerivedClass(string message) {
        Message = message; // Warning: Virtual member call in a constructor
    }

    public DerivedClass() : this("Default DerivedClass message") {}

    public override void PrintMessage() {
        Console.WriteLine("DerivedClass PrintMessage(): " + Message);
    }
}

I did find some other questions about this warning, but in those situations there is an actual call to a method. For instance, in this question, the answer by Matt Howels contains some sample code. I'll repeat it here for easy reference.

class Parent {
    public Parent() {
        DoSomething();
    }
    protected virtual void DoSomething() {};
}

class Child : Parent {
    private string foo;
    public Child() { foo = "HELLO"; }
    protected override void DoSomething() {
        Console.WriteLine(foo.ToLower());
    }
}

Matt doesn't describe on what error the warning would appear, but I'm assuming it will be on the call to DoSomething in the Parent constructor. In this example, I understand what is meant by a virtual member being called. The member call occurs in the base class, in which only a virtual method exists.

In my situation however, I don't see why assigning a value to Message would be calling a virtual member. Both the call to and the implementation of the Message property are defined in the derived class.

Although I can get rid of the error by making my Derived Class sealed, I would like to understand why this situation is resulting in the warning.

Update
Based on Brett's answer, I did my best to create a ChildClass derived from the DerivedClass that will ultimately result in an exception. This is what I came up with:

using System;

public class ChildClass : DerivedClass {
    private readonly string _foo;

    public ChildClass() : base("Default ChildClass Message") {
        _foo = "ChildClass foo";
    }

    public override string Message {
        get { return base.Message; }
        set {
            base.Message = value;
            Console.WriteLine(_foo.ToUpper() + " received " + value);
        }
    }
}

Off course it's a bit silly to be using _foo in the Message setter, but the point is that ReSharper doesn't see anything wrong with this class.

If however, you try to use the ChildClass in a program like this:

internal class Program {
    private static void Main() {
        var childClass = new ChildClass();
        childClass.PrintMessage();
    }
}

You'll get a NullReferenceException when creating the ChildClass object. The exception will be thrown by the ChildClass' attempt to use _foo.ToUpper() as _foo isn't initialized yet.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

可是我不能没有你 2024-10-18 11:18:47

这是因为您的 Message 属性可以被 class ChildClass : DerivedClass 覆盖 - 此时可以从 DerivedClass 中的构造函数调用 ChildClass 上的 Message 中的代码code>,并且您的 ChildClass 实例可能未完全初始化。

这就是为什么将 DerivedClass 密封可以解决问题 - 它无法被继承。

It's because your Message property can be overridden by class ChildClass : DerivedClass - at which point it's possible to invoke code in Message on ChildClass from the ctor in DerivedClass, and your ChildClass instance may not be full initialised.

That's why making your DerivedClass sealed solves the problem - it cannot be inherited.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文