0

我正在尝试编写 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 的任何实现的调用。

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

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

4

2 回答 2

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

  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())
        {
        }
    }
    
于 2013-02-08T12:28:49.023 回答
1

你不必复制任何东西。正如您所说,这些方法可能是静态的。所以制作它们并将结果传递给基类构造函数。

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

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

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

于 2013-02-08T12:20:13.697 回答