是否可以通过编程强制派生类将自身作为泛型类型传递给基类

本文关键字:泛型类型 基类 编程 可以通过 派生 是否 | 更新日期: 2023-09-27 18:13:34

基本上,我有以下场景:

public abstract class FooBase<T> where T : FooBase<T>
{
    public bool IsSpecial { get; private set; }
    public static T GetSpecialInstance()
    {
        return new T() { IsSpecial = true };
    }
}
public sealed class ConcreteFooA : FooBase<ConcreteFooA> { ... }
public sealed class ConcreteFooB : FooBase<ConcreteFooB> { ... }

但是,我在这里看到的问题是,我可以做ConcreteFooB : FooBase<ConcreteFooA> { ... },这会在运行时完全弄乱类(它不会满足我试图实现的逻辑),但仍然可以正确编译。

是否有一些方法我没有想到强制泛型,T,是任何派生类?


更新:我最终使用通用参数,T,在FooBase<T>类中,我只是没有列出每个方法,它作为输出和输入参数,但我确实有T的使用

是否可以通过编程强制派生类将自身作为泛型类型传递给基类

回答你的问题:

不,没有编译时解决方案来强制执行。

有两种方法可以执行此规则:

  1. 单元测试——你可以编写一个单元测试(或多个单元测试)来确保编译后的类型将自己作为泛型参数传入。
  2. 代码分析——你可以创建一个自定义的代码分析规则来执行它,然后将该规则设置为错误(而不是警告)。这将在编译时进行检查。
  3. FxCop规则——类似于代码分析规则,但如果你的Visual Studio没有内置代码分析支持,那么你可以使用FxCop。

当然,这些规则都不是在标准编译中强制执行的,而是需要额外的工具(单元测试、代码分析、FxCop)。如果有人拿了你的代码并在没有使用这些工具的情况下编译它,你就会遇到同样的问题……当然,在这种情况下,为什么其他人在没有运行单元测试或代码分析/FxCop规则的情况下编译您的代码?


或者,我不推荐这样做,您可以抛出一个运行时错误。为什么不呢?根据微软:

如果静态构造函数抛出异常,则运行时不会抛出异常第二次调用它时,类型将保持未初始化您的程序所在的应用程序域的生命周期运行。

那真的解决不了你的问题。最重要的是,在静态初始化期间抛出异常违反了代码分析CA1065:DoNotRaiseExceptionsInUnexpectedLocations。所以,如果你这样做,你就走错了方向。

据我所知,没有编译时的方法来强制执行这一点。但是,可以使用运行时检查来强制执行它。不寻常的用户操作通常不会导致这种情况(只是错误的编码),所以它类似于在地方有Debug.Assert(事实上,如果你喜欢,你可以使用它来实现它)。例如

public abstract class FooBase<T> where T : FooBase<T>
{
    protected FooBase()
    {
        Debug.Assert(this.GetType() == typeof(T));
    }
}

我不知道你为什么有这个要求。我首先建议你回头看看你的对象模型,确定为什么你觉得你需要这个需求,并确定是否有更好的方法来完成你想要实现的目标。

我想我看到了你上面的一个问题:在你的定义/类ConcreteFooAConcreteFooB的声明中没有泛型参数。

它看起来好像它可能是更好的你创建一个接口IFooBase和你的具体实现实现接口。在每个需要使用IFooBase的实例中,都应该使用IFooBase类型的变量。

:

public interface IFooBase { /* Interface contract... */ }
public class ConcreteFooA : IFooBase { /* Implement interface contract */ }
public class ConcreteFooB : IFooBase { /* Implement interface contract */ }
// Some class that acts on IFooBases
public class ActionClass
{
    public ActionClass(IFooBase fooBase) { this._fooBase = foobase };
    public DoSomething() { /* Do something useful with the FooBase */ }
    // Or, you could use method injection on static methods...
    public static void DoSomething(IFooBase fooBase) { /* Do some stuff... */ }
}

只是一些想法。但我不认为你可以完成你想要做的泛型单独。

这是不可能的,也不应该是,因为根据SOLID中的L:

Liskov替代原则:"程序中的对象应该可以被其子类型的实例替换,而不会改变程序的正确性"。

所以实际上编译器正在做它应该做的事情。

也许你需要改变你的类的设计和实现,例如通过使用行为模式。例如,如果一个对象应该为一个特定的计算提供不同的算法,你可以使用策略模式。

但是我不能给你建议,因为我不知道你到底想要达到什么。