Dreteh
Dreteh

Reputation: 387

Building a generic collection class

I am building the following class to manage a dictionary.

    public class EnumDictionary<TKey, TValue>
    {
        private Dictionary<TKey, TValue> _Dict;

        public EnumDictionary(Dictionary<TKey, TValue> Dict)
        {
            this._Dict = Dict;
        }

        public TKey GetValue(TValue value)
        {
            foreach (KeyValuePair<TKey, TValue> kvp in _Dict)
            {
                if (kvp.Value == value)
                    return kvp.Key;
            }

            throw new Exception("Undefined data type: " + value);
        }              
    }

But I am getting an error "Operator '==' cannot be applied to operands of type 'TValue' and 'TValue'".

BTW, I am making this custom collection is because my dictionary has unique value, but I can't get key by value from a dictionary.

Any help is appreciated. Thank you.

Upvotes: 2

Views: 630

Answers (7)

Jon Hanna
Jon Hanna

Reputation: 113232

If you want this to be general purpose, then you will want the definition of equality to be configurable, just as it is in the dictionary for keys.

Have a property of type IEqualityComparer<TValue>, which is set in the constructor.

Then have a version of the constructor that makes the default EqualityComparer<TValue>.Default. This will work by calling Equals on the type in question.

public class EnumDictionary<TKey, TValue>
{
    private Dictionary<TKey, TValue> _Dict;
    private readonly IEqualityComparer<TValue> _cmp;

    public EnumDictionary(Dictionary<TKey, TValue> Dict, IEqualityComparer<TValue> cmp)
    {
        this._Dict = Dict;
        _cmp = cmp;
    }
    public EnumDictionary(Dictionary<TKey, TValue> Dict)
        :this(Dict, IEqualityComparer<TValue>.Default){}

    public TKey GetValue(TValue value)
    {
        foreach (KeyValuePair<TKey, TValue> kvp in _Dict)
        {
            if (cmp.Equals(kvp.Value, value))
                return kvp.Key;
        }

        throw new Exception("Undefined data type: " + value);
    }              
}

Upvotes: 0

Dan Tao
Dan Tao

Reputation: 128317

Fredrik is right; you need to use Equals as you can't presume to be able to use == for all types, since the operator isn't defined for every type.

Depending on your scenario, it might also make sense to add

where TValue : IEquatable<TValue>

as a generic type constraint on your class. The reason for this is that object.Equals accepts another object as a parameter, which means that if TValue is a value type it will be boxed. If it can be known to implement IEquatable<TValue>, on the other hand, then Equals can be resolved to IEquatable<TValue>.Equals*, which takes a TValue as a parameter and therefore won't require value types to be boxed.

I might also recommend that you rethink the internal structure of this class. As it currently stands, there's no reason you need this class at all, as you could easily add an extension method to IDictionary<TKey, TValue> to find a key by value via enumeration over the values. What I would do instead is store two dictionaries: a Dictionary<TKey, TValue> and a Dictionary<TValue, TKey>, so that two-way lookup is possible in O(1).

*By the way, in case you're curious, the reason you can't use IEquatable<T> (or any interface for that matter) to ensure that a type has implemented the == operator is that operators are static, and interfaces cannot provide static methods (and thus can't provide operators).

Upvotes: 3

jgauffin
jgauffin

Reputation: 101130

Don't create a new class. Create a extension method:

public static class DictionaryHelper
{
    public static TKey GetKeyFromValue<TKey, TValue>(this IDictionary<TKey, TValue> instance, TValue value)
    {
        foreach (var kvp in instance)
        {
            if (kvp.Value.Equals(value))
                return kvp.Key;
        }
        return default(TKey);
    }
}

public class Example
{
    public static void Main(string[] argv)
    {
        Dictionary<string, string> test = new Dictionary<string, string> { { "Mykey", "MyValue" }, { "Key1", "Value2" } };
        string key = test.GetKeyFromValue("MyValue");
    }
}

Upvotes: 0

Marco
Marco

Reputation: 1346

Use the "where" condition on your generic types

class Dictionary<TKey,TVal>
    where TKey: IComparable, IEnumerable
    where TVal: MyI
{
    public void Add(TKey key, TVal val)
    {
    }
}

from http://msdn.microsoft.com/en-us/library/6b0scde8%28VS.80%29.aspx

Upvotes: 0

Chinjoo
Chinjoo

Reputation: 2792

you can use if (kvp.Value.Equals(value)) instead of ==.

Upvotes: 0

Fredrik M&#246;rk
Fredrik M&#246;rk

Reputation: 158289

Did you try using the Equals method?

if (kvp.Value.Equals(value))

I think this restriction is due to the fact that the == operator can't be used with all types. Take the following for instance:

struct Test
{
    public int Value;
}

Given the above struct, the following code will not compile:

Test a, b;
a = b = new Test();
bool areEqual = a == b; // Operator '==' cannot be applied to 
                        // operands of type 'Test' and 'Test'

However, all types have the Equals method, so calling that will work:

Test a, b;
a = b = new Test();
bool areEqual = a.Equals(b);

Upvotes: 5

Marthin
Marthin

Reputation: 6543

When you use generic comparsions I think you should implement a (x)CompareTo(Y) or a comparable class. Please correct me if im wrong.

Upvotes: 0

Related Questions