Поиск значений в словаре‹class1, class2› по свойству ключа

У меня есть словарь:

private Dictionary<MetaInfoValueGroupTag, List<object>> checkedMetainfoValues;

Как видите, класс Key — это MetaInfoValueGroupTag:

private class MetaInfoValueGroupTag
{
    private string metainfo;
    //...

    public string MetaInfo
    {
        get { return metainfo; }
        set { metainfo = value; }
    }
    //...
}

Мне нужно получить Value с помощью строкового объекта. Итак, мне нужно проверить, существует ли ключ в соответствии с string.

Это string значение представляет собой Metainfo property string содержимое MetaInfoValueGroupTag ключевого объекта словаря.

List<object> values = null;
this.checkedMetainfoValues.TryGetValue("metainfo_sample", out values);

Я не хочу писать это:

List<object> values = null;
this.checkedMetainfoValues.TryGetValue(new MetaInfoValueGroupTag("metainfo_sample"), 
                                       out values);

Я хочу избежать создания нового объекта дампа key для получения его значений.

Я полагаю, что должна быть возможна реализация IComparer или использование AnonymousComparer.


person Jordi    schedule 04.09.2015    source источник
comment
чтобы получить его значения - если значения семантически принадлежат строкам, а не MetaInfoValueGroupTags, почему бы не ввести словарь с помощью string ?   -  person AakashM    schedule 04.09.2015
comment
Мне нужно связать дополнительную информацию с каждой группой значений.   -  person Jordi    schedule 04.09.2015
comment
Разве вы не можете использовать Tuple‹MetaInfoValueGroupTag, List‹object›› в качестве значения? Таким образом, вы будете использовать Dictionary‹string, Tuple‹MetaInfoValueGroupTag, List‹object›››.   -  person piotrwest    schedule 04.09.2015
comment
Я думаю, какая разница... Неужели нельзя решить мой подход?   -  person Jordi    schedule 04.09.2015
comment
Лучше не использовать ключ для хранения информации. Используйте кортеж, предложенный @piotrwest, или создайте дополнительный класс, который обертывает ваши MetaInfoValueGroupTags и List<object>.   -  person CodingFeles    schedule 04.09.2015
comment
Есть ли реальная причина, по которой вы должны использовать MetaInfoValueGroupTags в качестве ключа, а не его строковое представление MetaInfo, учитывая тот факт, что он уникален? Используете ли вы какое-либо другое свойство внутри вашего класса для равенства?   -  person Yuval Itzchakov    schedule 04.09.2015
comment
@Jordi, если у вас есть дополнительная информация в ваших ключах, кроме MetaInfo, то new MetaInfoValueGroupTag("metainfo_sample") не будет работать, если только GetHashCode явно не использует только MetaInfo.   -  person CodingFeles    schedule 04.09.2015


Ответы (4)


Словарь так не работает. Он строит хеш-таблицу для ключей своих элементов и использует ее для быстрого поиска.

Он использует IEqualityComparer<T> для определения равенства ключей, но вы не можете использовать его для сравнения с другим типом, отличным от вашего MetaInfoValueGroupTag.

Однако вы можете использовать метод расширений LINQ и использовать тот факт, что Dictionary — это IEnumerable. Это означает, что вы можете перебрать его поэлементно и найти то, что вам нужно. Здесь вы можете использовать собственный компаратор или просто лямбда-предикат.

Чтобы использовать метод расширений, вам нужно добавить для них использование:

using System.Linq;

После этого вы можете использовать следующий метод со своим словарем, чтобы найти один элемент:

var searchedElement = checkedMetainfoValues.SingleOrDefault(x=>x.Key.MetaInfo == "metainfo_sample");

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

В конце концов, лучше изменить ваш словарь, чтобы использовать string в качестве ключа или, если это невозможно, просто создать объект MetaInfoValueGroupTag и использовать его в качестве ключа.

Но имейте в виду, что это зависит от алгоритма хеширования для MetaInfoValueGroupTag. Возможно, значение хеша будет зависеть не только от значения MetaInfo, поэтому new MetaInfoValueGroupTag("metainfo_sample") может не подходить для вашего поиска.

Подробнее: https://msdn.microsoft.com/en-us/library/xfhwa508(v=VS.110).aspx

person CodingFeles    schedule 04.09.2015

Вы можете получить всю пару ключ/значение следующим образом:

var entry = checkedMetainfoValues.Where(x => x1.Key.MetaInfo == "metainfo_sample").Single();
person Bgl86    schedule 04.09.2015

Я предполагаю, что строковое значение уникально, иначе ваш доступ по строке не будет работать. Предлагаю поменять ключ на строку.

private Dictionary<string, List<object>> checkedMetainfoValues;

// access by meta info:
MetaInfoValueGroupTag metaInfoGroupTag;
var value = checkedMetaInfoValues[metaInfoGroupTag.MetaInfo]

// access by string:
string metaInfo;
var value = checkedMetaInfoValues[metaInfo]
person Stefan Steinegger    schedule 04.09.2015

Если вам нужен именно этот синтаксис:

List<object> values = null;
this.checkedMetainfoValues.TryGetValue("metainfo_sample", out values);

Вы можете создать свой собственный словарь, который наследуется от обычного словаря, следующим образом:

private class MyStrangeDictionary : Dictionary<MetaInfoValueGroupTag, List<object>>
{
    public MyStrangeDictionary() : base(MetaInfoValueGroupTag.MetainfoComparer)
    {
    }

    public List<object> this[string key]
    {
        get
        {
            return this[new MetaInfoValueGroupTag {MetaInfo = key}];
        }
        //set
        //{
        //    Add(new MetaInfoValueGroupTag { MetaInfo = key }, value);
        //} 
    }

    public bool TryGetValue(string key, out List<object> value)
    {
        if (ContainsKey(new MetaInfoValueGroupTag {MetaInfo = key}))
        {
            value = this[key];
            return true;
        }
        value = default(List<object>);
        return false;
    }
}

Использование будет следующим:

_checkedMetainfoValues = new MyStrangeDictionary();

_checkedMetainfoValues.Add(new MetaInfoValueGroupTag { MetaInfo = "f"}, new List<object> { "first"});
_checkedMetainfoValues.Add(new MetaInfoValueGroupTag { MetaInfo = "s"}, new List<object> { "second", "2"});

var test = _checkedMetainfoValues["f"];
foreach (var t in test)
{
    Console.WriteLine(t);
}

List<object> test1;
_checkedMetainfoValues.TryGetValue("s", out test1);
foreach (var t in test1)
{
    Console.WriteLine(t);
}

РЕДАКТИРОВАТЬ:

Для полноты вот ваш класс вместе с компаратором:

private class MetaInfoValueGroupTag
{
    private sealed class MetainfoEqualityComparer : IEqualityComparer<MetaInfoValueGroupTag>
    {
        public bool Equals(MetaInfoValueGroupTag x, MetaInfoValueGroupTag y)
        {
            if (ReferenceEquals(x, y)) return true;
            if (ReferenceEquals(x, null)) return false;
            if (ReferenceEquals(y, null)) return false;
            if (x.GetType() != y.GetType()) return false;
            return string.Equals(x.metainfo, y.metainfo);
        }

        public int GetHashCode(MetaInfoValueGroupTag obj)
        {
            return (obj.metainfo != null ? obj.metainfo.GetHashCode() : 0);
        }
    }

    private static readonly IEqualityComparer<MetaInfoValueGroupTag> MetainfoComparerInstance = new MetainfoEqualityComparer();

    public static IEqualityComparer<MetaInfoValueGroupTag> MetainfoComparer
    {
        get { return MetainfoComparerInstance; }
    }

    private string metainfo;
    //...

    public string MetaInfo
    {
        get { return metainfo; }
        set { metainfo = value; }
    }
    //...
}
person piotrwest    schedule 04.09.2015
comment
Вы предлагаете ему сделать обертку, которая будет делать то, чего он старается избегать. В вопросе указано, что он хотел избежать создания объекта только для использования его в качестве ключа. - person CodingFeles; 04.09.2015
comment
Мы не знаем, хотел ли он избежать создания объекта из-за синтаксиса (может быть, он хочет выставить этот API в библиотеке) или из-за соображений производительности. В первом случае такой подход окей, ИМХО. - person piotrwest; 04.09.2015
comment
Я думаю, что ключевой объект dump подразумевает это. Но, конечно, я мог неправильно понять это. По-прежнему определять один тип как ключ и фактически использовать другой — это плохо. - person CodingFeles; 04.09.2015