具有两个不相等(唯一)类型的泛型类

时间:2011-11-08 20:19:18

标签: c# generics constraints

是否可以实现一个约束为两个唯一泛型参数的类?

如果不是,是因为它没有实现,还是因为语言结构(继承)是不可能的?

我想要一些形式:

class BidirectionalMap<T1,T2> where T1 != T2
{
  ...
}

我正在实施Bidirectional dictionary。这主要是好奇心问题,而非需要。


从评论中解释:

  1. Dan:“如果不满足这种约束会有什么负面后果?”

  2. 我:“然后用户可以使用map [t1]和map [t2]进行索引。如果它们是相同的类型,则没有任何区别,也没有任何意义。”

  3. Dan:编译器实际上允许[两个泛型类型参数来定义不同的方法重载],所以我很好奇; 是否随意选择其中一种方法进行调用?

6 个答案:

答案 0 :(得分:4)

扩展示例以突出显示问题:

public class BidirectionalMap<T1,T2>
{
    public void Remove(T1 item) {}
    public void Remove(T2 item) {}

    public static void Test()
    {
        //This line compiles
        var possiblyBad = new BidirectionalMap<string, string>();

        //This causes the compiler to fail with an ambiguous invocation
        possiblyBad.Remove("Something");
    }
}

所以答案是,即使你不能指定约束T1!= T2,也没关系,因为一旦你尝试做违反隐式约束的事情,编译器就会失败。它仍然在编译时捕获失败,因此您可以使用这些重载而不受惩罚。这有点奇怪,因为你可以创建一个地图实例(甚至可以编写适当操作地图的IL代码),但C#编译器不会让你通过任意解决模糊的重载来造成严重破坏。


一方面注意,如果你不小心,这种超载可能会导致一些奇怪的行为。如果您有BidirectionalMap<Animal, Cat>和Cat:Animal,请考虑使用此代码会发生什么:

Animal animal = new Cat();
map.Remove(animal);

这将调用占用Animal的重载,因此它会尝试删除一个键,即使您可能打算删除值Cat。这是一个有点人为的情况,但是当由于方法重载导致出现非常不同的行为时,这足以引起注意。在这种情况下,如果您只是给方法指定不同的名称,反映它们的不同行为(RemoveKey和RemoveValue,那么就更容易阅读和维护。)

答案 1 :(得分:0)

类型约束似乎用词不当。虽然它们确实违反了类型参数,但目的是让编译器知道该类型可用的操作 如果你想,你可以有一个约束,其中T1和T2都来自单独的具体基类,但我认为这不是你想要的。

答案 2 :(得分:0)

不等式无助于编译器捕获错误。当您在类型参数上指定约束时,您告诉编译器此类型的变量将始终支持某个接口或将以某种方式运行。其中每一个都允许编译器验证更多类似于“此方法将存在以便可以在T上调用”的内容。

类型参数的不等式更像是验证方法参数不为空。它是程序逻辑的一部分,而不是它的类型安全性。

答案 3 :(得分:0)

我不完全确定为什么这是一个理想的编译时间检查。通过装箱键或值可以基本上绕过条件,从而使编译时检查无效。

需要考虑一些因素来确定......我试图阻止哪些错误?

如果您只是阻止懒惰的同事不阅读文档,则添加仅调试检查并抛出异常。这样就可以删除检查以获取发布代码,例如

#if Debug

if (T1 is T2 || T2 is T1)
{
    throw new ArguementException(...);
}

#endif

如果您试图阻止恶意的人以非预期的方式使用您的库,那么可能需要进行运行时检查,否则可以轻松打开密钥或值。

答案 4 :(得分:0)

不,您不能将等式(或不等式)用作约束。简单地说,平等不是约束,而是条件。您应该在构造函数中测试条件,例如类型的相等或不等式,并抛出适当的异常。

答案 5 :(得分:-1)

没有对类型本身施加任何其他限制,没有有效的方法。正如其他人所说,你可以制定两种类型派生自两个不同基类的约束,但从设计的角度来看,这可能不是很好。

编辑补充说:没有实现这个的原因很可能是因为微软的任何人都没有考虑过这样的事情在编译时需要强制执行,这与其他与你实际能够如何实现的约束不同。使用指定类型的变量。正如一些评论者指出的那样,您当然可以在运行时强制执行此操作。