Словарь, в котором значение является анонимным типом в C #

Возможно ли в C # создать System.Collections.Generic.Dictionary<TKey, TValue>, где TKey - безусловный класс, а TValue - анонимный класс с рядом свойств, например, имя столбца базы данных и его локализованное имя.

Что-то вроде этого:

new { ID = 1, Name = new { Column = "Dollar", Localized = "Доллар" } }

person abatishchev    schedule 24.10.2009    source источник
comment
Очень похоже (с linq): Общий список анонимного класса   -  person nawfal    schedule 28.06.2014
comment
.Select (...). AsEnumerable (). ToDictionary (k = ›k.id, v =› v as object) работал у меня. Моя переменная была Dictionary ‹long, object›   -  person Ravi    schedule 23.10.2014
comment
@Ravishankar: Скорее всего, тебе здесь не нужен AsEnumerable(). Он ничего не добавляет поверх Select().   -  person abatishchev    schedule 23.10.2014
comment
@abatishchev. Истинный. Я хотел сказать, что как объект облегчил мне жизнь. Остальную часть кода можно проигнорировать.   -  person Ravi    schedule 31.10.2014
comment
@Ravi object не анонимный тип.   -  person BrainSlugs83    schedule 04.06.2021


Ответы (5)


Вы не можете напрямую объявить такой тип словаря (есть кладжи, но они предназначены только для развлечения и новизны), но если ваши данные поступают из IEnumerable или IQueryable источника, вы можете получить его с помощью оператора LINQ ToDictionary() и проецирования требуемый ключ и (анонимно введенное) значение из элементов последовательности:

var intToAnon = sourceSequence.ToDictionary(
    e => e.Id,
    e => new { e.Column, e.Localized });
person itowlson    schedule 24.10.2009

Как сказал itowlson , вы не можете объявить такого зверя, но вы действительно можете создать его:

static IDictionary<TKey, TValue> NewDictionary<TKey, TValue>(TKey key, TValue value)
{
    return new Dictionary<TKey, TValue>();
}

static void Main(string[] args)
{
    var dict = NewDictionary(new {ID = 1}, new { Column = "Dollar", Localized = "Доллар" });
}

Непонятно, зачем вам на самом деле использовать такой код.

person Ðаn    schedule 25.10.2009
comment
Почему? Я могу придумать несколько причин. Вот один. Рассмотрим, например, словарь, используемый для запоминания n-мерной функции, скажем, функции четырех аргументов типа int. В следующей версии CLR вы, конечно, просто будете использовать 4-кортеж, но в C # 3 вы можете создать словарь анонимного типа {int, int, int, int}, готово, нет необходимости определять свой собственный тип кортежа. - person Eric Lippert; 25.10.2009
comment
Непонятно, зачем вам это нужно? Как насчет составных ключей (например, новый {State = NY, Country = US}) - person Quarkly; 04.01.2019
comment
Или составные результаты, относящиеся к ссылочному типу (новые встроенные кортежи являются типами значений). - person BrainSlugs83; 04.06.2021

Я думаю, что ASP.NET MVC не выходил в то время, когда был задан этот вопрос. Он действительно конвертирует анонимные объекты в словари внутренне.

Взгляните, например, на HtmlHelper class. Метод, который переводит объекты в словари, - это _ 2_. Однако он специфичен для MVC и возвращает RouteValueDictionary.

Если вам нужно что-то более общее, попробуйте следующее:

public static IDictionary<string,object> AnonymousObjectToDictionary(object obj)
{
    return TypeDescriptor.GetProperties(obj)
        .OfType<PropertyDescriptor>()
        .ToDictionary(
            prop => prop.Name,
            prop => prop.GetValue(obj)
        );
}

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

И вот одна общая версия:

public static IDictionary<string,T> AnonymousObjectToDictionary<T>(
    object obj, Func<object,T> valueSelect
)
{
    return TypeDescriptor.GetProperties(obj)
        .OfType<PropertyDescriptor>()
        .ToDictionary<PropertyDescriptor,string,T>(
            prop => prop.Name,
            prop => valueSelect(prop.GetValue(obj))
        );
}
person jpbochi    schedule 04.02.2012
comment
Спасибо, что сообщили об этом! Недостатком такого метода я могу назвать использование отражения. - person abatishchev; 05.02.2012
comment
@abatishchev Думаю, без размышлений решить проблему невозможно. Я не уверен, но чтение свойств с помощью TypeDescriptor может быть более эффективным, чем с обычным .GetType (). GetProperties (). Кстати, я использовал ILSpy на HtmlHelper, и он делает почти то же самое. - person jpbochi; 05.02.2012
comment
object не анонимный тип. Это противоположно тому, что мы хотим. - Конечным результатом является словарь со строгой типизацией, не содержащий анонимных типов. - person BrainSlugs83; 04.06.2021

Вы можете сделать рефлекс

public static class ObjectExtensions
{
    /// <summary>
    /// Turn anonymous object to dictionary
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public static IDictionary<string, object> ToDictionary(this object data)
    {
        var attr = BindingFlags.Public | BindingFlags.Instance;
        var dict = new Dictionary<string, object>();
        foreach (var property in data.GetType().GetProperties(attr))
        {
            if (property.CanRead)
            {
                dict.Add(property.Name, property.GetValue(data, null));
            }
        }
        return dict;
    }
}
person noneno    schedule 19.08.2010
comment
Хорошая идея. Также вы можете сделать его общим: public static IDictionary<string, T> ToDictionary(this object data) { } - person abatishchev; 20.08.2010

Если вы хотите инициализировать пустой словарь, вы можете сделать что-то вроде этого:

var emptyDict = Enumerable
    .Empty<(int, string)>()
    .ToDictionary(
         x => new { Id = x.Item1 }, 
         x => new { Column = x.Item2, Localized = x.Item2});

По сути, вам просто нужен пустой enumerable с кортежем, который имеет типы, которые вы хотите использовать в своих окончательных анонимных типах, а затем вы можете получить пустой словарь, который набран так, как вы хотите.

Если хотите, можете также назвать типы в кортеже:

var emptyDict = Enumerable
            .Empty<(int anInt, string aString)>()
            .ToDictionary(
                x => new { Id = x.anInt },
                x => new { Column = x.aString, Localized = x.aString});
person Kevek    schedule 09.08.2019