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

本文关键字:抽象 基类 方法 派生 构造函数 调用 | 更新日期: 2023-09-27 17:58:14

我正在尝试对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实现,这就是我重写它的原因。但是,每个派生类都必须执行我放入基类构造函数中的代码,包括对派生类具有的MakeInput布局实现的调用。

我试图尽可能避免代码重复,但微软建议我永远不应该在基类构造函数中调用可重写函数,即使任何可重写的实现都不依赖于运行时设置的值(从技术上讲,如果c允许我们重写statics,它们可能被标记为static)。

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

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

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

  2. 使用助手接口/类/委托来避免构造函数虚拟方法调用:

    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())
        {
        }
    }
    

您不必四处复制任何内容。正如您所说,这些方法可能是静态的。因此,制作它们并将结果传递给基类构造函数。

当前的代码并没有使这一切变得简单,因为MakeInputLayout依赖于在基类的构造函数中创建的值。虽然你也可以以某种方式提取出来,但我认为这会变得一团糟。

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

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

相关文章: