Словарь, где значение является анонимным типом в C#
Можно ли в C# создать System.Collections.Generic.Dictionary<TKey, TValue>
где TKey
безусловный класс и TValue
- анонимный класс с рядом свойств, например - имя столбца базы данных и его локализованное имя.
Что-то вроде этого:
new { ID = 1, Name = new { Column = "Dollar", Localized = "Доллар" } }
4 ответа
Вы не можете объявить такой тип словаря напрямую (есть kludges, но они предназначены только для развлечения и новизны), но если ваши данные поступают из источника IEnumerable или IQueryable, вы можете получить их, используя оператор LINQ ToDictionary и проецируя требуемый ключ и (анонимно набранное) значение из элементов последовательности:
var intToAnon = sourceSequence.ToDictionary(
e => e.Id,
e => new { e.Column, e.Localized });
Как сказал 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 = "Доллар" });
}
Непонятно, почему вы действительно хотите использовать такой код.
Я думаю, что ASP.NET MVC не завершился, когда был задан этот вопрос. Он конвертирует анонимные объекты в словари внутри страны.
Просто взгляните на HtmlHelper
класс, например. Метод, который переводит объекты в словари AnonymousObjectToHtmlAttributes
, Это специфицировано в 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))
);
}
Вы можете сделать восстановление
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;
}
}
Если вы хотите инициализировать пустой словарь, вы можете сделать что-то вроде этого:
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});