是否可以通过编程强制派生类将自身作为泛型类型传递给基类
本文关键字:泛型类型 基类 编程 可以通过 派生 是否 | 更新日期: 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的使用
回答你的问题:
不,没有编译时解决方案来强制执行。
有两种方法可以执行此规则:
- 单元测试——你可以编写一个单元测试(或多个单元测试)来确保编译后的类型将自己作为泛型参数传入。
- 代码分析——你可以创建一个自定义的代码分析规则来执行它,然后将该规则设置为错误(而不是警告)。这将在编译时进行检查。
- 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));
}
}
我不知道你为什么有这个要求。我首先建议你回头看看你的对象模型,确定为什么你觉得你需要这个需求,并确定是否有更好的方法来完成你想要实现的目标。
我想我看到了你上面的一个问题:在你的定义/类ConcreteFooA
和ConcreteFooB
的声明中没有泛型参数。
它看起来好像它可能是更好的你创建一个接口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替代原则:"程序中的对象应该可以被其子类型的实例替换,而不会改变程序的正确性"。
所以实际上编译器正在做它应该做的事情。
也许你需要改变你的类的设计和实现,例如通过使用行为模式。例如,如果一个对象应该为一个特定的计算提供不同的算法,你可以使用策略模式。
但是我不能给你建议,因为我不知道你到底想要达到什么。