是否有可能以编程方式强制派生类将自身作为泛型类型传递给基类?

时间:2013-05-14 12:56:56

标签: c# generics

基本上,我有以下情况:

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,无论派生类是什么?


更新:我最终在FooBase<T>类中使用泛型参数T,我只是没有列出每个将它作为out和in参数的方法,但我确实有用吨。

5 个答案:

答案 0 :(得分:3)

回答你的问题:

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

答案 1 :(得分:1)

有几种方法可以强制执行此规则:

  1. 单元测试 - 您可以编写单元测试(或单元测试)以确保已编译的类型作为通用参数传入。
  2. 代码分析 - 您可以创建一个强制执行此操作的自定义代码分析规则,然后将该规则设置为错误(vs警告)。这将在编译时检查。
  3. FxCop规则 - 与代码分析规则类似,除非您没有内置支持代码分析的Visual Studio版本,否则您可以使用FxCop。
  4. 当然,这些规则都不是针对标准编译强制执行的,而是需要额​​外的工具(单元测试,代码分析,FxCop)。如果有人拿走你的代码并在不使用这些工具的情况下编译它们你会遇到同样的问题......当然,为什么有人在没有运行你的单元测试或Code Analysis / FxCop规则的情况下编译你的代码?


    或者,我不建议这样做,你可能会抛出一个运行时错误。为什么不?根据微软的说法:

      

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

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

答案 2 :(得分:1)

据我所知,没有编译时方法来强制执行此操作。但是,可以使用运行时检查来强制执行。没有异常的用户操作通常会导致这种情况(只是不正确的编码),因此它类似于在某些地方使用Debug.Assert(事实上,如果您愿意,可以使用它实现它)。 E.g。

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

答案 3 :(得分:0)

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

我想我发现上面的内容存在一个问题:类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... */ }
}

只是一些想法。但我认为你不能单独使用Generics来完成你想做的事情。

答案 4 :(得分:0)

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

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

实际上,编译器正在按照它的意图去做。

也许您需要更改类的设计和实现,例如使用行为模式。例如,如果对象应为特定计算提供不同的算法,则可以使用Strategy Pattern

但我不能就此提出建议,因为我不知道你想要达到什么目的。