为什么通用类型约束不可继承/分层实施

Rob*_*nik 26 c# inheritance generic-constraints

物品类

public class Item
{
    public bool Check(int value) { ... }
}
Run Code Online (Sandbox Code Playgroud)

具有泛型类型约束的基本抽象类

public abstract class ClassBase<TItem>
    where TItem : Item
{
    protected IList<TItem> items;

    public ClassBase(IEnumerable<TItem> items)
    {
        this.items = items.ToList();
    }    

    public abstract bool CheckAll(int value);
}
Run Code Online (Sandbox Code Playgroud)

没有约束的继承类

public class MyClass<TItem> : ClassBase<TItem>
{
    public override bool CheckAll(int value)
    {
        bool result = true;
        foreach(TItem item in this.items)
        {
            if (!item.Check(value)) // this doesn't work
            {
                result = false;
                break;
            }
        }
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

我想知道为什么泛型类型约束不可继承?因为如果我的继承类继承自基类并传递其对基类具有约束的泛型类型,则它自动意味着继承类中的泛型类型应具有相同的约束而不显式定义它.不应该吗?

我做错了什么,理解它是错的还是通用类型约束真的不可继承?如果后者是真的,为什么世界就是这样呢?

一点额外的解释

为什么我认为在类上定义的泛型类型约束应该在子类上继承或强制执行?让我给你一些额外的代码,使它不那么明显.

假设我们按上述方式拥有所有三个类.然后我们也有这个类:

public class DanteItem
{
    public string ConvertHellLevel(int value) { ... }
}
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,这个类不会继承,Item因此它不能用作具体的类ClassBase<DanteItem>(忘记ClassBase现在是抽象的事实.它也可以是常规类).由于MyClass没有为其泛型类型定义任何约束,因此似乎完全有效MyClass<DanteItem>...

但.这就是为什么我认为泛型类型约束应该继承/强制继承类,就像成员泛型类型约束一样,因为如果我们看一下MyClass它的定义就说:

MyClass<T> : ClassBase<T>
Run Code Online (Sandbox Code Playgroud)

TDanteItem我们可以看到,它自动不能与使用MyClass,因为它是从继承ClassBase<T>DanteItem不履行泛型类型约束.我可以说**泛型类型MyClass取决于ClassBase泛型类型约束,因为否则MyClass可以用任何类型实例化.但我们知道它不可能.

当我将其MyClass定义为:当然会有所不同:

public class MyClass<T> : ClassBase<Item>
Run Code Online (Sandbox Code Playgroud)

在这种情况下,T与基类'泛型类型没有任何关系,因此它独立于它.

这是一个有点长的解释/推理.我可以简单地总结一下:

如果我们不MyClass对它提供泛型类型约束,则暗示我们可以MyClass使用任何具体类型进行实例化.但我们知道这是不可能的,因为它MyClass是继承自的ClassBase,并且具有泛型类型约束.

我希望现在这更有意义.

Eri*_*ert 42

另一个更新:

这个问题是我2013年7月的博客主题.谢谢你这个好问题!

更新:

我已经给了这个更多的想法,我认为问题在于你根本不需要继承.相反,您想要的是所有必须放在类型参数上的约束,以便将该类型参数用作另一种类型的类型参数,以便自动推导并无形地添加到类型参数的声明中. 是?

一些简化的例子:

class B<T> where T:C {}
class D<U> : B<U> {}
Run Code Online (Sandbox Code Playgroud)

U是一个类型参数,在必须为C的上下文中使用.因此在您看来,编译器应该推导出并自动在U上设置C的约束.

那这个呢?

class B<T, U> where T : X where U : Y {}
class D<V> : B<V, V> {}
Run Code Online (Sandbox Code Playgroud)

现在V是一个在上下文中使用的类型参数,它必须是X和Y.因此在您看来,编译器应该推导出并自动将X和Y的约束放在V上.是吗?

那这个呢?

class B<T> where T : C<T> {}
class C<U> : B<D<U>> where U : IY<C<U>> {}
class D<V> : C<B<V>> where V : IZ<V> {}
Run Code Online (Sandbox Code Playgroud)

我刚刚做到了,但我向你保证,这是一个完全合法的类型层次结构.请描述一个清晰且一致的规则,该规则不会进入无限循环以确定T,U和V上的所有约束.不要忘记处理类型参数已知为引用类型且接口约束具有的情况协方差或逆变注释!此外,无论B,C和D在源代码中出现什么顺序,算法都必须具有完全相同结果的属性.

如果推理约束是你想要的功能,那么编译器必须能够处理这样的情况并在不能的情况下给出明确的错误消息.

基类型有什么特别之处?为什么不一直实现该功能呢?

class B<T> where T : X {}
class D<V> { B<V> bv; }
Run Code Online (Sandbox Code Playgroud)

V是在上下文中使用的类型参数,它必须可以转换为X; 因此,编译器应该推导出这个事实,并在X上设置X的约束.是的?或没有?

为什么字段特殊?那这个呢:

class B<T> { static public void M<U>(ref U u) where U : T {} }
class D<V> : B<int> { static V v; static public void Q() { M(ref v); } }
Run Code Online (Sandbox Code Playgroud)

V是在上下文中使用的类型参数,它只能是int.因此,C#编译器应该推导出这个事实并自动在V上设置int的约束.

是?没有?

你看到这是怎么回事?它停在哪里?为了正确实现所需的功能,编译器必须进行全程序分析.

编译器没有进行这种级别的分析,因为这会将购物车放在马前.当你构建一个通用的,必须证明你已经满足了约束的编译器.编译器的工作不是弄清楚你想要说什么,而是找出进一步的约束条件满足原始约束条件.

出于类似的原因,编译器也不会尝试代表您自动推断接口中的方差注释.有关详细信息,请参阅有关该主题的文章

http://blogs.msdn.com/b/ericlippert/archive/2007/10/29/covariance-and-contravariance-in-c-part-seven-why-do-we-need-a-syntax-at- all.aspx


原始答案:

我想知道为什么泛型类型约束不可继承?

只有成员才能继承.约束不是成员.

如果我的继承类继承自基类并传递其对基类具有约束的泛型类型,则它自动意味着继承类中的泛型类型应该具有相同的约束而不显式定义它.不应该吗?

你只是断言应该是什么,而不提供任何解释为什么它应该是那样的.向我们解释为什么你认为世界应该是这样的; 有什么好处,有什么缺点,成本是多少?

我做错了什么,理解它是错的还是通用类型约束真的不可继承?

通用约束不会被继承.

如果后者是真的,为什么世界就是这样呢?

默认情况下,功能"未实现".我们不必提供实现功能的原因!除非有人花钱实施,否则每项功能都不会实施.

现在,我必须注意,泛型类型约束方法继承的.方法是成员,成员是继承的,约束是方法的一部分(虽然不是其签名的一部分).因此约束随着继承的方法而出现.当你说:

class B<T> 
{
    public virtual void M<U>() where U : T {}
}

class D<V> : B<IEnumerable<V>>
{
    public override void M<U>() {}
}
Run Code Online (Sandbox Code Playgroud)

然后D<V>.M<U>继承约束并替换IEnumerable<V>T; 因此约束是U必须可转换为IEnumerable<V>.请注意,C#不允许您重新声明约束.在我看来,这是一种错误; 为了清晰起见,我希望能够重申这一约束.

但是D并没有从B 继承对T的任何约束; 我不明白它怎么可能.M是B 的成员,由D及其约束继承.但是T首先不是B的成员,那么继承什么呢?

我真的不明白你想要什么功能.你能解释一下更多细节吗?

  • @RobertKoritnik:当然有猜测.您建议编译器*猜测开发人员打算在那里对B*中的T进行约束.如果开发人员*不打算以任何方式限制B <T>的用户,该怎么办?开发人员可以选择解决问题的方法:将约束放在B上,删除A上的约束,或让B不从A派生.您希望编译器猜测正确的解决方案是第一个.现在,假设为了论证,99%的时间*是*正确的解决方案.**有多少错误的节目是1%?** (2认同)