在构造函数中调用派生方法的抽象基类?

时间:2013-02-08 12:16:34

标签: c# inheritance polymorphism dry abstract

我正在尝试编写DRY,我有以下设置,Visual Studio的代码分析系统告诉我这是不明智的:

public abstract class ShaderBase
{

    protected ShaderBase(Device device, string vertexShaderString, string pixelShaderString)
    {
        ShaderSignature inputSignature;
        using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile(vertexShaderString, "VShader", "vs_4_0", ShaderFlags.None, EffectFlags.None))
        {
            vertexShader = new VertexShader(device, bytecode);
            inputSignature = ShaderSignature.GetInputSignature(bytecode);
        }

        inputLayout = MakeInputLayout(device,inputSignature);
    }

    protected abstract InputLayout MakeInputLayout(Device device, ShaderSignature inputSignature);
}

public class TextureShader:ShaderBase
{
    public ColourShader(Device device) : base(device,"shaders/colour.fx", "shaders/colour.fx")
    {
    }

    protected override InputLayout MakeInputLayout(Device device, SlimDX.D3DCompiler.ShaderSignature inputSignature)
    {
        return new InputLayout(device, inputSignature, new[] { 
            new InputElement("POSITION", 0, SlimDX.DXGI.Format.R32G32B32_Float, 0), 
            new InputElement("COLOR",0,SlimDX.DXGI.Format.R32G32B32_Float,0)
        });
    }
}

你可以看到,我有一个基类,我有多个派生类。每个派生类使用不同的InputLayout,因此必须使用不同的MakeInputLayout实现,这就是我重写它的原因。但是,每个派生类都必须执行我放在基类构造函数中的代码,包括调用派生类所具有的MakeInputLayout实现。

我试图在可能的情况下避免代码重复,但Microsoft建议我永远不应该在基类构造函数中调用可重写的函数,即使没有任何覆盖实现依赖于在运行时设置的值(它们可以,技术上,如果c#允许我们覆盖静态,则标记为静态。

我想知道的是,让基类强制派生类在其构造函数中调用自己派生的函数实现的可接受方式是什么?或者我只是需要复制并过去一些代码并降低系统的可维护性?

2 个答案:

答案 0 :(得分:4)

  1. 如果您100%确定在您的情况下不会出现问题,则可以忽略Visual Studio的代码分析。但是,它可能不是很安全,我个人不建议这样做。

  2. 使用辅助接口/ classes / delegates来避免构造函数虚方法调用:

    public interface IInputLayoutMaker
    {
        InputLayout MakeInputLayout(Device device, SlimDX.D3DCompiler.ShaderSignature inputSignature);
    }
    
    public abstract class ShaderBase
    {
        protected ShaderBase(Device device, string vertexShaderString, string pixelShaderString, IInputLayoutMaker inputLayoutMaker)
        {
            ShaderSignature inputSignature;
            using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile(vertexShaderString, "VShader", "vs_4_0", ShaderFlags.None, EffectFlags.None))
            {
                vertexShader = new VertexShader(device, bytecode);
                inputSignature = ShaderSignature.GetInputSignature(bytecode);
            }
    
            inputLayout = inputLayoutMaker.MakeInputLayout(device,inputSignature);
        }
    
        protected abstract InputLayout MakeInputLayout(Device device, ShaderSignature inputSignature);
    }
    
    public class TextureShader:ShaderBase
    {
        private class TextureShaderInputLayoutMaker : IInputLayoutMaker
        {
            public InputLayout MakeInputLayout(Device device, SlimDX.D3DCompiler.ShaderSignature inputSignature)
            {
                return new InputLayout(device, inputSignature, new[] { 
                    new InputElement("POSITION", 0, SlimDX.DXGI.Format.R32G32B32_Float, 0), 
                    new InputElement("COLOR",0,SlimDX.DXGI.Format.R32G32B32_Float,0)
                });
            }
        }
    
        public ColourShader(Device device) : base(device,"shaders/colour.fx", "shaders/colour.fx", new TextureShaderInputLayoutMaker())
        {
        }
    }
    

答案 1 :(得分:1)

您无需复制任何内容。如你所说,这些方法可能是静态的。所以创建它们并将结果传递给基类构造函数。

您拥有的当前代码并不能使一切变得简单,因为MakeInputLayout依赖于在基类的构造函数中创建的值。虽然你可以以某种方式提取它,但我认为这会变得混乱。

因此,我提出了一个不同的方法:

创建一个IInputLayoutProvider接口及其实现,并将其传递给基类。