A dictionary where value is an anonymous type in C#

37,495

Solution 1

You can't declare such a dictionary type directly (there are kludges but these are for entertainment and novelty purposes only), but if your data is coming from an IEnumerable or IQueryable source, you can get one using the LINQ ToDictionary() operator and projecting out the required key and (anonymously typed) value from the sequence elements:

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

Solution 2

As itowlson said, you can't declare such a beast, but you can indeed create one:

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 = "Доллар" });
}

It's not clear why you'd actually want to use code like this.

Solution 3

You can do a refection

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

Solution 4

I think ASP.NET MVC didn't exit at the time this question was made. It does convert anonymous objects to dictionaries internally.

Just take a look at the HtmlHelper class, for example. The method that translates objects to dictionaries is the AnonymousObjectToHtmlAttributes. It it's specifc to MVC and returns an RouteValueDictionary, however.

If you want something more generic, try this:

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

One intersting advantages of this implementation is that it returns an empty dictionary for null objects.

And here's one generic version:

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

Solution 5

If you would like to initialize an empty dictionary you could do something like the this:

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

Basically you just need an empty enumerable with a tuple that has the types you want to use in your final anonymous types and then you can get an empty dictionary that is typed the way you'd like.

If you wanted to you could name the types in the tuple as well:

var emptyDict = Enumerable
            .Empty<(int anInt, string aString)>()
            .ToDictionary(
                x => new { Id = x.anInt },
                x => new { Column = x.aString, Localized = x.aString});
Share:
37,495
abatishchev
Author by

abatishchev

This is my GUID. There are many like it but this one is mine. My GUID is my best friend. It is my life. I must master it as I must master my life. Without me, my GUID is useless. Without my GUID I am useless.

Updated on July 09, 2022

Comments

  • abatishchev
    abatishchev almost 2 years

    Is it possible in C# to create a System.Collections.Generic.Dictionary<TKey, TValue> where TKey is unconditioned class and TValue - an anonymous class with a number of properties, for example - database column name and it's localized name.

    Something like this:

    new { ID = 1, Name = new { Column = "Dollar", Localized = "Доллар" } }
    
  • Eric Lippert
    Eric Lippert over 14 years
    Why? I can think of a number of reasons. Here's one. Consider for example a dictionary used to memoize an n-ary function, say a function of four int arguments. In the next version of the CLR of course you'd just use a 4-tuple, but in C# 3, you could create a dictionary of anonymous type {int, int, int, int}, done, no need to define your own tuple type.
  • abatishchev
    abatishchev over 13 years
    Good idea. Also you can make it generic: public static IDictionary<string, T> ToDictionary(this object data) { }
  • abatishchev
    abatishchev over 12 years
    Thanks for letting that know! The disadvantage of such method I may name is using reflection.
  • jpbochi
    jpbochi over 12 years
    @abatishchev I think it's impossible to solve the problem without reflection. I'm not sure, but reading properties with TypeDescriptor might be more eficient than with a regular .GetType().GetProperties(). BTW, I used ILSpy on HtmlHelper and it does almost exactly the same thing.
  • Quarkly
    Quarkly over 5 years
    Not clear why you'd want this? How about compound keys (e.g. new { State = "NY", Country = "US" })
  • BrainSlugs83
    BrainSlugs83 almost 3 years
    Or compound results that are in a reference type (the new inline tuples are value types).
  • BrainSlugs83
    BrainSlugs83 almost 3 years
    object is not an anonymous type. This does the opposite of what we want. -- The end result is a strong typed dictionary that contains no anonymous types.
  • C Perkins
    C Perkins over 2 years
    The tuple is unnecessary and creates a false sense of dependency on the tuple. The key trick here is to call the extension method ToDictionary(), but that can be accomplished using ANY empty enumerable, then assigning literal values to the anonymous type for type inference. Like this: var emptyDictx = Enumerable.Empty<object>().ToDictionary(_ => 0, _ => new { Column = string.Empty, Localized = string.Empty }); Notice that even the lamba parameters can be discards.
  • jpbochi
    jpbochi almost 2 years
    the input is object, the output is IDictionary<string,T>, which is not anonymous. T can be whatever you want. Are people seriously downvoting this answer after 10 years, without adding any extra context? Could you please have the decency of saying what you think is wrong with it?