2010-07-03 43 views
9

我正在编写bijective dictionary class,但我想确保两种泛型类型不是相同的类型有两个原因。使用何处指定不同的泛型

首先,我想它来实现在两个方向上IDictionary接口,但

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

给我“ 'BijectiveDictionary < TKEY的,TValue >' 不能同时实现 '的IDictionary < TKEY的,TValue >' 和“IDictionary的< TValue,TKEY的>”,因为他们可以结合一些类型参数替换“(这是可以理解的,但不可取。)

其次,我想编写一个选择如果两种类型都相同,则可以使用模型化解

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)

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)映射32的欢迎,并[3]将返回2

然而,Jaroslav Jandek's solution建议使用第二个字典来做这个,当TKey!= TValue。虽然这对于那些情况(以及我最终决定实施的内容)而言非常有效,但当TKey = TValue时,通过允许Add(2,3); Add(3,4)将单个密钥3映射为两个值(2 in一个方向,另一个方向4),尽管我严格相信仍然是一个有效的双射函数。

+0

您使用的是字典呢?你甚至需要你的字典中的Surjection部分吗?也就是说,我的答案中的代码处理所有的情况(实现IDictonary,提供双注并处理TKey == TValue的情况)。 – 2010-07-04 10:49:45

+2

如果你想双向注入'TKey == TValue',你可以使用'SelfBijectiveDictionary :BijectiveDictionary {...}'并重写它的'Add'方法并首先检查if(base.Contains(key) | this.Reversed.Contains(value))throw ...'。 无论如何,类应该有不同的命名,因为一个是双射'X-> Y'和另一个'S-> S'(双射本身)。 – 2010-07-06 17:54:59

+0

指出'X-> Y'和'S-> S'之间的区别有很大帮助。对不起所有的困惑!是的 - 我为他们制作了两个单独命名的字典。谢谢你的帮助。 – dlras2 2010-07-06 19:11:51

回答

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

注意:显式强制转换'Console.WriteLine(((目标字典< int, int>)a)[true]);当集合类型相同时,**不起作用。这是显而易见的原因 - 无论如何我都想指出。 – 2010-07-04 08:08:48

2

总之,没有。重新优化它,其中一个选项可能是基于静态初始化器的策略,它为同一类型的案例选择适当的具体实现,否则;即

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

但自己使用MakeGenericType和反射创建实例,你几乎肯定会找到(但是一旦创建它应该是罚款 - 你只能做一次)。

-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? 

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

+0

你无法做到这一点,因为这会打破字典的双重性质(除非我误解双射,这是可能的。)'dict.Add(3,4);'会犯错,因为3已经映射到2. – dlras2 2010-07-03 21:32:31

+0

@ Vilx-:的确,这就是为什么我使用另一种方法。用我的例子,你会得到隐含的4和明确的2. – 2010-07-03 21:32:36

+0

@Daniel Rasmussen:3没有映射到2. 3被映射到4.你必须明白,来自集合A的3不同于来自集合B的3 '。 我明确地区分了2组(可通过引用指针检测到)。 – 2010-07-03 21:40:45

2

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

它并不统一,实际上它可能比它更好,因为你可以分开单独的界面。

在这里看到我的帖子,在另一个上下文中有一个完整的工作示例。 https://stackoverflow.com/a/12361409/471129

基本上,你要做的就是添加其他类型的参数来IIndexer,使之成为IIndexer <TKey, TValue, TDifferentiator>

然后,当你使用它两次,你通过“第一个”一号使用,和“第二”的第2使用

所以,类测试变为:class Test<TKey, TValue> : IIndexer<TKey, TValue, First>, IIndexer<TValue, TKey, Second>

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

,其中第一,二和琐碎的是:

interface First { } 

interface Second { } 
+0

似乎令人讨厌,但它的工作原理。 +1 – 2014-10-11 02:53:33

相关问题