43

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 = "Доллар" } }
abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • Very similar (with linq): [A generic list of anonymous class](http://stackoverflow.com/questions/612689/a-generic-list-of-anonymous-class) – nawfal Jun 28 '14 at 09:01
  • .Select(...).AsEnumerable().ToDictionary(k => k.id, v => v as object) worked for me. My variable was Dictionary – Ravi Oct 23 '14 at 19:07
  • @Ravishankar: You don't need `AsEnumerable()` here, most probably. It doesn't add anything on top of `Select()`. – abatishchev Oct 23 '14 at 19:13
  • @abatishchev. True. I wanted to say that "as object" made my life easier. Rest of the code can be ignored. – Ravi Oct 30 '14 at 23:14
  • @Ravi `object` is not an anonymous type. – BrainSlugs83 Jun 04 '21 at 17:37

5 Answers5

48

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 });
Ðаn
  • 10,934
  • 11
  • 59
  • 95
itowlson
  • 73,686
  • 17
  • 161
  • 157
20

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.

Community
  • 1
  • 1
Ðаn
  • 10,934
  • 11
  • 59
  • 95
  • 12
    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. – Eric Lippert Oct 25 '09 at 00:50
  • Not clear why you'd want this? How about compound keys (e.g. new { State = "NY", Country = "US" }) – Quark Soup Jan 03 '19 at 21:15
  • Or compound results that are in a reference type (the new inline tuples are value types). – BrainSlugs83 Jun 04 '21 at 17:43
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;
    }
}
noneno
  • 3,314
  • 1
  • 16
  • 5
3

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))
        );
}
jpbochi
  • 4,366
  • 3
  • 34
  • 43
  • Thanks for letting that know! The disadvantage of such method I may name is using reflection. – abatishchev Feb 04 '12 at 20:28
  • @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. – jpbochi Feb 04 '12 at 20:48
  • `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. – BrainSlugs83 Jun 04 '21 at 17:45
  • the input is `object`, the output is `IDictionary`, 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? – jpbochi Jun 13 '22 at 08:29
2

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});
Kevek
  • 2,534
  • 5
  • 18
  • 29
  • 2
    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().ToDictionary(_ => 0, _ => new { Column = string.Empty, Localized = string.Empty });` Notice that even the lamba parameters can be discards. – C Perkins Sep 09 '21 at 04:38