使用Where指定不同的泛型

时间:2010-07-03 19:46:46

标签: c# .net generics

我正在编写bijective dictionary class,但我想确保这两种泛型不是同一类型,原因有两个。

首先,我希望它在两个方向上实现IDictionary接口,但是

public class BijectiveDictionary<TKey, TValue>
    : IDictionary<TKey, TValue>, IDictionary<TValue, TKey>

给我“'BijectiveDictionary&lt; TKey,TValue&gt;'无法实现'IDictionary&lt; TKey,TValue&gt;'和'IDictionary&lt; TValue,TKey&gt;'因为它们可能统一某些类型参数替换“(这是可以理解的,但不可取的。)

其次,如果两种类型相同,我想写一个优化的解决方案。

public class BijectiveDictionary<TKey, TValue>
    : IDictionary<TKey, TValue> where TValue : TKey
{
    // Optimized solution
}

public class BijectiveDictionary<TKey, TValue>
    : IDictionary<TKey, TValue>, IDictionary<TValue, TKey> where TValue : !TKey
{
    // Standard solution
}

这可能吗?

如果没有,我可以考虑不实施IDictionary,但我不能保证TValue this[TKey key]TKey this[TValue key]会有所不同,这是不幸的。


这里的问题似乎是当两种类型相同时,会出现特殊情况。

我最初的意图是创建一个字典,将一个键恰好映射到一个值,反之亦然,这样每KeyValuePair<TKey, TValue>(X, Y)KeyValuePair<TValue, TKey>(Y, X)也存在。{/ p>

TKey = TValue时,这可以简化为单个词典:

public T this[T key]
{
    get { return this[key]; }
    set
    {
        base.Add(key, value);
        base.Add(value, key);
    }
}

在这种情况下,您不能Add(2,3); Add(3,4),因为Add(2,3)也会将3映射到2,而[3]会返回2。< / p>

但是,Jaroslav Jandek's solution建议使用第二个词典为TKey!= TValue的情况执行此操作。虽然这对于那些情况非常有用,(以及最终我决定实施的内容)但TKey = TValue时,Add(2,3); Add(3,4) = 3并不完全符合我原来的意图将单个键2映射到两个值(4在一个方向上,{{1}}在另一个方向上,但我认为严格来说仍然是一个有效的双射函数。

4 个答案:

答案 0 :(得分:3)

这个(不同方法)怎么样:

public class BijectiveDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public BijectiveDictionary<TValue, TKey> Reversed { get; protected set; }

    public BijectiveDictionary()
    {
        this.Reversed = new BijectiveDictionary<TValue,TKey>(true);
        this.Reversed.Reversed = this;
    }

    protected BijectiveDictionary(bool reversedWillBeSetFromTheCallingBiji) { }

    protected void AddRaw(TKey key, TValue value)
    {
        base.Add(key, value);
    }

    // Just for demonstration - you should implement the IDictionary interface instead.
    public new void Add(TKey key, TValue value)
    {
        base.Add(key, value);
        this.Reversed.AddRaw(value, key);
    }

    public static explicit operator BijectiveDictionary<TValue, TKey>(BijectiveDictionary<TKey, TValue> biji)
    {
        return biji.Reversed;
    }
}

并在代码中:

BijectiveDictionary<int, bool> a = new BijectiveDictionary<int, bool>();

a.Add(5, true);
a.Add(6, false);

Console.WriteLine(a[5]);// => True
Console.WriteLine(((BijectiveDictionary < bool, int>)a)[true]);// => 5
//or
Console.WriteLine(a.Reversed[true]);// => 5

答案 1 :(得分:2)

简而言之,没有。重新优化它,一个选项可能是基于静态初始化程序的策略,为相同类型的情况选择适当的具体实现,否则;即。

public class BijectiveDictionary<TKey, TValue>
    : IDictionary<TKey, TValue>
{
    static readonly ISomeStrategy strategy;
    static BijectiveDictionary() {
        if(typeof(TKey) == typeof(TValue)) {
            strategy = ...
        } else {
            strategy = ...
        }
    }
}

但你几乎肯定会发现自己使用MakeGenericType和反射来创建实例(但是一旦创建它应该没问题 - 而且你只做了一次)。

答案 2 :(得分:2)

在某种程度上,这可以做到!我使用差异化方法,而不是限制类型的限定符。

它没有统一,事实上它可能比它更好,因为你可以分开挑逗单独的接口。

在此处查看我的帖子,在另一个上下文中提供完整的工作示例。 https://stackoverflow.com/a/12361409/471129

基本上,您要做的是向IIndexer添加另一个类型参数,使其成为IIndexer <TKey, TValue, TDifferentiator>

然后当你使用它两次时,你将“First”传递给第一次使用,将第二次传递给第二次使用

因此,课程测试成为:class Test<TKey, TValue> : IIndexer<TKey, TValue, First>, IIndexer<TValue, TKey, Second>

因此,您可以new Test<int,int>()

第一和第二是微不足道的:

interface First { }

interface Second { }

答案 3 :(得分:-1)

即使你可以,这段代码的输出是什么?

var dict = new BijectiveDictionary<int, int>();

dict.Add(2,3);
dict.Add(3,4);

Console.WriteLine(dict[3]); // Do I get 2 or 4?

也许你可以用另一种不模糊的方式重构你的代码?