views:

114

answers:

7

Hi all,

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.

+5  A: 

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);
Fredrik Mörk
kvp.Value.Equals(value) works. Thanks.
Dreteh
A: 

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

Marthin
You only need this if you require < or > comparisons. If you do then you should implement the IComparable interface and the CompareTo() method. Otherwise the Equals() method (inherited from Object and possibly overridden) works fine.
Xavier Poinas
Thanks for clearing this up for me. Was a while since i worked with this kind of thing.
Marthin
A: 

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

Marco
A: 

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

Chinjoo
+3  A: 

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).

Dan Tao
Did you try it? Curiously I cannot get it work unless I'm adding the constraint: `where TValue : class` - http://ideone.com/qdG73 . `IEquatable<TValue>` isn't compiling either.
Kobi
@Kobi: Can't get what to work? It sounds like you've misunderstood me; you could get `==` to work if you add the `where TValue : class` constraint because the `==` is defined implicitly for all reference types (it'll just use `object.ReferenceEquals`). What I'm suggesting in my answer is that the OP add `where TValue : IEquatable<TValue>` so that **the `Equals` method** won't box its argument. And yes, I did try this, and it works for me as expected. Is that clear, or am I missing something?
Dan Tao
@Kobi: Yes, I looked at your link and it seems you thought I was saying "if you add `where TValue : IEquatable<TValue>` you can use the `==` operator"... but that's not what I was saying. Hopefully that's clearer now? Or do you think I should reword my answer?
Dan Tao
Obviously, I misunderstood you. Sorry, and thanks for the extra explanation.
Kobi
A: 

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");
    }
}
jgauffin
A: 

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);
    }              
}
Jon Hanna