解决'构造函数中的虚方法调用'问题 [英] Solving the 'Virtual method call in constructor' issue

查看:96
本文介绍了解决'构造函数中的虚方法调用'问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用c#编写软件。我正在使用一个抽象类,指令,它包含以下代码:

I am making a software in c#. I am using an abstract class, Instruction, that has these bits of code:

protected Instruction(InstructionSet instructionSet, ExpressionElement newArgument,
    bool newDoesUseArgument, int newDefaultArgument, int newCostInBytes, bool newDoesUseRealInstruction) {

    //Some stuff

    if (DoesUseRealInstruction) {
        //The warning appears here.
        RealInstruction = GetRealInstruction(instructionSet, Argument);
    }
}

public virtual Instruction GetRealInstruction(InstructionSet instructionSet, ExpressionElement argument) {
    throw new NotImplementedException("Real instruction not implemented. Instruction type: " + GetType());
}

所以Resharper告诉我,在标记的行上,我正在调用虚拟方法在构造函数中,这很糟糕。我理解构造函数被调用的顺序。 GetRealInstruction 方法的所有覆盖内容如下所示:

So Resharper tells me that at the marked line I am 'calling a virtual method in constructor' and that this is bad. I understand the thing about the order in which the constructors are called. All overrides of the GetRealInstruction method look like this:

public override Instruction GetRealInstruction(InstructionSet instructionSet, ExpressionElement argument) {
    return new GoInstruction(instructionSet, argument);
}

所以他们不依赖于班级中的任何数据;他们只返回依赖于派生类型的东西。 (所以构造函数顺序不会影响它们。)

So they don't depend on any data in the class; they just return something that depends on the derived type. (so the constructor order doesn't affect them).

那么,我应该忽略它吗?我宁愿不;所以任何人都可以告诉我如何避免这个警告?

So, should I ignore it? I'd rather not; so could anyone show me how could I avoid this warning?

我不能巧妙地使用委托,因为 GetRealInstruction 方法有还有一次重载。

I cannot use delegates neatly because the GetRealInstruction method has one more overload.

推荐答案

我已经遇到过这个问题很多次了,这是我找到正确解决的最佳方法它是将从构造函数调用的虚方法抽象为一个单独的类。然后,您将此新类的实例传递到原始抽象类的构造函数中,每个派生类将其自己的版本传递给基础构造函数。解释这有点棘手,所以我将根据你的例子给出一个例子。

I've encountered this issue quite a few times and the best way I've found to properly resolve it is to abstract the virtual method that is being called from the constructor, into a separate class. You would then pass an instance of this new class into the constructor of your original abstract class, with each derived class passing it's own version to the base constructor. It's a bit tricky to explain so I'll give an example, based on yours.

public abstract class Instruction
{
    protected Instruction(InstructionSet instructionSet, ExpressionElement argument, RealInstructionGetter realInstructionGetter)
    {
        if (realInstructionGetter != null)
        {
            RealInstruction = realInstructionGetter.GetRealInstruction(instructionSet, argument);
        }
    }

    public Instruction RealInstruction { get; set; }

    // Abstracted what used to be the virtual method, into it's own class that itself can be inherited from.
    // When doing this I often make them inner/nested classes as they're not usually relevant to any other classes.
    // There's nothing stopping you from making this a standalone class of it's own though.
    protected abstract class RealInstructionGetter
    {
        public abstract Instruction GetRealInstruction(InstructionSet instructionSet, ExpressionElement argument);
    }
}

// A sample derived Instruction class
public class FooInstruction : Instruction
{
    // Passes a concrete instance of a RealInstructorGetter class
    public FooInstruction(InstructionSet instructionSet, ExpressionElement argument) 
        : base(instructionSet, argument, new FooInstructionGetter())
    {
    }

    // Inherits from the nested base class we created above.
    private class FooInstructionGetter : RealInstructionGetter
    {
        public override Instruction GetRealInstruction(InstructionSet instructionSet, ExpressionElement argument)
        {
            // Returns a specific real instruction
            return new FooRealInstuction(instructionSet, argument);
        }
    }
}

// Another sample derived Instruction classs showing how you effictively "override" the RealInstruction that is passed to the base class.
public class BarInstruction : Instruction
{
    public BarInstruction(InstructionSet instructionSet, ExpressionElement argument)
        : base(instructionSet, argument, new BarInstructionGetter())
    {
    }

    private class BarInstructionGetter : RealInstructionGetter
    {
        public override Instruction GetRealInstruction(InstructionSet instructionSet, ExpressionElement argument)
        {
            // We return a different real instruction this time.
            return new BarRealInstuction(instructionSet, argument);
        }
    }
}

在您的特定示例中,它确实如此得到一点混乱,我开始用尽合理的名字,但这是因为你已经在指令中嵌入了一些指令,即一个指令有一个RealInstruction(或者至少可选);但是你可以看到它仍然可以达到你想要的效果并避免构造函数的任何虚拟成员调用。

In your particular example it does get a little confusing and I started to run out of sensible names, but this is due to the fact you already have a nesting of Instructions within Instructions, i.e. an Instruction has a RealInstruction (or at least optionally does); but as you can see it is still possible to achieve what you want and avoid any virtual member calls from a constructor.

如果仍然不清楚,我也会基于我最近在我自己的代码中使用的一个示例。在这种情况下,我有两种类型的表单,标题表单和消息表单,两者都从基本表单继承。所有表单都有字段,但每个表单类型都有不同的机制来构造它的字段,所以我最初有一个名为GetOrderedFields的抽象方法,我从基础构造函数调用,并且方法在每个派生表单类中都被覆盖。这给了我你提到的resharper警告。我的解决方案与上面的模式相同,如下所示

In case that's still not clear, I'll also give an example based on one I used just recently in my own code. In this case I have 2 types of forms, a header form and a message form, both of which inherit from a base form. All forms have fields but each form type has a different mechanism for constructing its fields, so I originally had an abstract method called GetOrderedFields that I called from the base constructor and the method was overriden in each derived form class. This gave me the resharper warning you mention. My solution was the same pattern as above and is as follows

internal abstract class FormInfo
{
    private readonly TmwFormFieldInfo[] _orderedFields;

    protected FormInfo(OrderedFieldReader fieldReader)
    {
        _orderedFields = fieldReader.GetOrderedFields(formType);
    }

    protected abstract class OrderedFieldReader
    {
        public abstract TmwFormFieldInfo[] GetOrderedFields(Type formType);
    }
}

internal sealed class HeaderFormInfo : FormInfo
{
    public HeaderFormInfo()
        : base(new OrderedHeaderFieldReader())
    {
    }

    private sealed class OrderedHeaderFieldReader : OrderedFieldReader
    {
        public override TmwFormFieldInfo[] GetOrderedFields(Type formType)
        {
            // Return the header fields
        }
    }
}

internal class MessageFormInfo : FormInfo
{
    public MessageFormInfo()
        : base(new OrderedMessageFieldReader())
    {
    }

    private sealed class OrderedMessageFieldReader : OrderedFieldReader
    {
        public override TmwFormFieldInfo[] GetOrderedFields(Type formType)
        {
            // Return the message fields
        }
    }
}

这篇关于解决'构造函数中的虚方法调用'问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆