web-dev-qa-db-fra.com

Un dictionnaire où value est un type anonyme en C #

Est-il possible en C # de créer un System.Collections.Generic.Dictionary<TKey, TValue>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 = "Доллар" } }
38
abatishchev

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

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.

18
Ðаn

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

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;
    }
}
3
noneno

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