开发者

Using Where to specify different generics

开发者 https://www.devze.com 2023-01-06 02:50 出处:网络
I\'m writing a bijective dictionary class, but I want to ensure the two generic types are not the same type for two reasons.

I'm writing a bijective dictionary class, but I want to ensure the two generic types are not the same type for two reasons.

Firstly, I would like it to implement the IDictionary interface in both directions, but

public class BijectiveDictionary<TKey, TValue>
    : IDictionar开发者_运维问答y<TKey, TValue>, IDictionary<TValue, TKey>

gives me " 'BijectiveDictionary<TKey,TValue>' cannot implement both 'IDictionary<TKey,TValue>' and 'IDictionary<TValue,TKey>' because they may unify for some type parameter substitutions " (which is understandable, but undesirable.)

Secondly, I would like to write an optimized solution if both types are the same.

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
}

Is this possible?

If not, I can consider not implementing IDictionary, but I couldn't guarantee TValue this[TKey key] and TKey this[TValue key] would be different, which would be unfortunate.


It looks like the problem here is that when the two types are the same, the special cases arise.

My original intent was to create a dictionary which maps exactly one key to exactly one value, and visa versa, such that for every KeyValuePair<TKey, TValue>(X, Y), a KeyValuePair<TValue, TKey>(Y, X) exists as well.

When TKey = TValue, then this can be simplified down to a single dictionary:

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

In this case, you cannot Add(2,3); Add(3,4) because Add(2,3) maps 3 to 2 as well, and [3] would return 2.

However, Jaroslav Jandek's solution proposed using a second dictionary to do this for cases when TKey != TValue. And although this works wonderfully for those cases, (and what I decided to implement, in the end) it doesn't quite follow my original intent when TKey = TValue, by allowing Add(2,3); Add(3,4) to map a single key 3 to two values (2 in one direction, and 4 in the other,) though I believe strictly speaking is still a valid bijective function.


How about this (different approach):

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;
    }
}

and in code:

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


In short, no. Re optimising it, one option there might be a strategy based on a static initializer, that chooses an appropriate concrete implementation for the same-type case, but otherwise; i.e.

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

but you will almost certainly find yourself using MakeGenericType and reflection to create the instance (but once created it should be fine - and you only do that once).


To some extent, this can be done! I use a differentiating method, instead of qualifier(s) limiting the types.

It does not unify, in fact it might be better than if it did because you can tease the separate interfaces apart.

See my post here, with a fully working example in another context. https://stackoverflow.com/a/12361409/471129

Basically, what you do is add another type parameter to IIndexer, so that it become IIndexer <TKey, TValue, TDifferentiator>.

Then you when you use it twice, you pass "First" to the 1st use, and "Second" for the 2nd use

So, class Test becomes: class Test<TKey, TValue> : IIndexer<TKey, TValue, First>, IIndexer<TValue, TKey, Second>

Thus, you can do new Test<int,int>()

where First, and Second are trivial:

interface First { }

interface Second { }


Even if you could, what would be the output of this code?

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

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

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

Maybe you can restructure your code in another way which isn't ambiguous?

0

精彩评论

暂无评论...
验证码 换一张
取 消