5

基本上,我有以下情况:

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,我只是没有列出将它作为输出和输入参数的每个方法,但我确实对 T 有用。

4

5 回答 5

3

要回答您的问题:

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

于 2013-05-14T13:03:43.227 回答
1

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

  1. 单元测试 - 您可以编写一个单元测试(或单元测试)以确保编译的类型将自己作为通用参数传递。
  2. 代码分析 - 您可以创建一个自定义代码分析规则来强制执行此操作,然后将该规则设置为错误(相对于警告)。这将在编译时进行检查。
  3. FxCop 规则 - 类似于代码分析规则,除非您没有内置支持代码分析的 Visual Studio 版本,否则您可以改用 FxCop。

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


或者,我不推荐这样做,您可能会引发运行时错误。为什么不?据微软称:

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

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

于 2013-05-14T13:12:21.277 回答
1

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

public abstract class FooBase<T> where T : FooBase<T>
{
    protected FooBase()
    {
        Debug.Assert(this.GetType() == typeof(T));
    }
}
于 2013-05-14T13:16:03.500 回答
0

I don't know why you have this as a requirement. I would first suggest that you go back and look at 'your object model and determine why you feel you need this requirement and determine if there's a better way to accomplish whatever it is you're trying to achieve.

I think I see one problem with what you have above: no generic parameters in your definitions/declarations of classes ConcreteFooA and ConcreteFooB.

It looks as though it may be better for you to create an interface IFooBase and have your concrete implementations implement the interface. In every instance where you want to work with an IFooBase, you'd use a variable of type IFooBase.

So:

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... */ }
}

Just some ideas. But I don't think you can accomplish what you want to do with Generics alone.

于 2013-05-14T13:06:09.947 回答
0

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

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

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

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

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

于 2013-05-14T13:31:16.483 回答