Est-il possible en C # de créer un System.Collections.Generic.Dictionary<TKey, TValue>
où TKey
est une classe non conditionnée et TValue
- une classe anonyme avec un certain nombre de propriétés, par exemple - le nom de la colonne de la base de données et son nom localisé.
Quelque chose comme ça:
new { ID = 1, Name = new { Column = "Dollar", Localized = "Доллар" } }
Vous ne pouvez pas déclarer un tel type de dictionnaire directement (il existe des kludges mais ceux-ci sont uniquement à des fins de divertissement et de nouveauté), mais si vos données proviennent d'une source IEnumerable
ou IQueryable
, vous pouvez obtenez-en un en utilisant l'opérateur LINQ ToDictionary()
et en projetant la clé requise et la valeur (tapée de manière anonyme) à partir des éléments de séquence:
var intToAnon = sourceSequence.ToDictionary(
e => e.Id,
e => new { e.Column, e.Localized });
Comme itowlsondit , vous ne pouvez pas déclarer une telle bête, mais vous pouvez en effet créer un:
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 = "Доллар" });
}
On ne sait pas pourquoi vous voudriez réellement utiliser du code comme celui-ci.
Je pense qu'ASP.NET MVC n'est pas sorti au moment où cette question a été posée. Il convertit les objets anonymes en dictionnaires en interne.
Jetez un œil à la HtmlHelper
class , par exemple. La méthode qui traduit les objets en dictionnaires est AnonymousObjectToHtmlAttributes
. Il est spécifique à MVC et renvoie un RouteValueDictionary
, cependant.
Si vous voulez quelque chose de plus générique, essayez ceci:
public static IDictionary<string,object> AnonymousObjectToDictionary(object obj)
{
return TypeDescriptor.GetProperties(obj)
.OfType<PropertyDescriptor>()
.ToDictionary(
prop => prop.Name,
prop => prop.GetValue(obj)
);
}
Un avantage intéressant de cette implémentation est qu'elle renvoie un dictionnaire vide pour les objets null
.
Et voici une version générique:
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))
);
}
Vous pouvez faire une réflexion
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;
}
}
Si vous souhaitez initialiser un dictionnaire vide, vous pouvez faire quelque chose comme ceci:
var emptyDict = Enumerable
.Empty<(int, string)>()
.ToDictionary(
x => new { Id = x.Item1 },
x => new { Column = x.Item2, Localized = x.Item2});
Fondamentalement, vous avez juste besoin d'un énumérable vide avec un tuple qui a les types que vous souhaitez utiliser dans vos types anonymes finaux, puis vous pouvez obtenir un dictionnaire vide qui est tapé comme vous le souhaitez.
Si vous le souhaitez, vous pouvez également nommer les types dans le tuple:
var emptyDict = Enumerable
.Empty<(int anInt, string aString)>()
.ToDictionary(
x => new { Id = x.anInt },
x => new { Column = x.aString, Localized = x.aString});