解决'构造函数中的虚方法调用'问题

Edw*_* B. 19 c# resharper inheritance constructor

我在c#中制作软件.我正在使用一个抽象类,Instruction它有这些代码:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

因此,Resharper告诉我,在标记的行中,我"在构造函数中调用虚方法"并且这很糟糕.我理解构造函数被调用的顺序.该GetRealInstruction方法的所有覆盖内容如下所示:

public override Instruction GetRealInstruction(InstructionSet instructionSet, ExpressionElement argument) {
    return new GoInstruction(instructionSet, argument);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Ric*_*ard 19

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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在你的特定例子中,它确实有点令人困惑,我开始用尽合理的名字,但这是因为你已经在指令中嵌入了一些指令,即一个指令有一个RealInstruction(或至少可选) ; 但正如您所看到的,仍然可以实现您想要的并避免构造函数中的任何虚拟成员调用.

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

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)