Создание общего класса коллекции - PullRequest
2 голосов
/ 07 сентября 2010

Я создаю следующий класс для управления словарем.

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

Но я получаю сообщение об ошибке «Оператор == не может быть применен к операндам типа« TValue »и« TValue »».

Кстати, я делаю эту пользовательскую коллекцию потому, что мой словарь имеет уникальное значение, но я не могу получить ключ по значению из словаря.

Любая помощь приветствуется. Спасибо.

Ответы [ 7 ]

5 голосов
/ 07 сентября 2010

Вы пытались использовать метод Equals?

if (kvp.Value.Equals(value))

Я думаю, что это ограничение связано с тем, что оператор == нельзя использовать со всеми типами. Возьмем, к примеру, следующее:

struct Test
{
    public int Value;
}

Учитывая приведенную выше структуру, следующий код не будет компилироваться:

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

Однако все типы имеют метод Equals, поэтому вызов будет работать:

Test a, b;
a = b = new Test();
bool areEqual = a.Equals(b);
3 голосов
/ 07 сентября 2010

Фредрик прав ; вам нужно использовать Equals, поскольку вы не можете предположить, что сможете использовать == для всех типов, поскольку оператор не определен для каждого типа.

В зависимости от вашего сценария, возможно, имеет смысл добавить

where TValue : IEquatable<TValue>

как ограничение общего типа для вашего класса. Причина этого в том, что object.Equals принимает другой object в качестве параметра, что означает, что если TValue является типом значения, он будет помещен в коробку. Если известно, что, с другой стороны, можно реализовать IEquatable<TValue>, тогда Equals может быть преобразован в IEquatable<TValue>.Equals*, который принимает TValue в качестве параметра и, следовательно, не требует упаковывания типов значений.

Я мог бы также рекомендовать вам переосмыслить внутреннюю структуру этого класса. В настоящее время этот класс вообще не нужен, так как вы можете легко добавить метод расширения к IDictionary<TKey, TValue>, чтобы найти ключ по значению с помощью перечисления значений. Вместо этого я бы сохранил два словаря: Dictionary<TKey, TValue> и Dictionary<TValue, TKey>, чтобы в O (1) был возможен двусторонний поиск.

* Кстати, если вам интересно, причина, по которой вы не можете использовать IEquatable<T> (или какой-либо другой интерфейс в этом отношении), чтобы убедиться, что тип реализовал оператор ==, заключается в том, что операторы статические , и интерфейсы не могут предоставлять статические методы (и, следовательно, не могут предоставлять операторы).

0 голосов
/ 07 сентября 2010

Если вы хотите, чтобы это было общим назначением, то вы захотите, чтобы определение равенства можно было настраивать так же, как в словаре для ключей.

Имеет свойство типа IEqualityComparer<TValue>, которое устанавливается в конструкторе.

Тогда есть версия конструктора, которая делает значение по умолчанию EqualityComparer<TValue>.Default. Это будет работать, вызывая Equals для данного типа.

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);
    }              
}
0 голосов
/ 07 сентября 2010

Не создавайте новый класс. Создайте метод расширения:

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");
    }
}
0 голосов
/ 07 сентября 2010

вы можете использовать if (kvp.Value.Equals (value)) вместо ==.

0 голосов
/ 07 сентября 2010

Используйте условие "где" для ваших родовых типов

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

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

0 голосов
/ 07 сентября 2010

Когда вы используете общие сравнения, я думаю, что вы должны реализовать (x) CompareTo (Y) или сопоставимый класс.Пожалуйста, поправьте меня, если я ошибаюсь.

...