64

I had the following problem today, and I was wondering if there is a solution for my problem.

My idea was to build anonymous classes and use it as a datasource for a WinForm BindingSource:

public void Init()
{
    var option1 = new
                  {
                      Id = TemplateAction.Update,
                      Option = "Update the Templates",
                      Description = "Bla bla 1."
                  };

    var option2 = new
                  {
                      Id = TemplateAction.Download,
                      Option = "Download the Templates",
                      Description = "Bla bla 2."
                  };

    var list = new[] {option1, option2}.ToList();

    bsOptions.DataSource = list; // my BindingSource

    // cboTemplates is a ComboBox
    cboTemplates.DataSource = bsOptions; 
    cboTemplates.ValueMember = "Id";
    cboTemplates.DisplayMember = "Option";

    lblInfoTemplates.DataBindings.Add("Text", bsOptions, "Description");
}

That works fine so far.

The problem I had is to get Id out of the "Current" property of the BindingSource, because I can't cast it back to the Anonymous Type:

private void cmdOK_Click(object sender, EventArgs e)
{
    var option = (???)bsOptions.Current;
}

I guess there is no way to find out the type of "Current" and access the "Id" Property? Maybe someone has a good solution...

I know there are other (and also better) ways to get the Id (Reflection, reading the value from the ComboBox, not using anonymous tpyes,...) I'm just courious if it's possible to get the Type out of bsOptions.Current in an elegant way.

mkj
  • 2,761
  • 5
  • 24
  • 28
gsharp
  • 27,557
  • 22
  • 88
  • 134

6 Answers6

103

Note, as per the comment, I'd just like to point out that I too recommend using a real type when you need to pass it around the program like this. Anonymous types should only really be used locally in a single method at a time (in my opinion), but anyway, here's the rest of my answer.


You can do it using a trick, by tricking the compiler into inferring the right type for you:

using System;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new { Id = 1, Name = "Bob" };
            TestMethod(a);

            Console.Out.WriteLine("Press enter to exit...");
            Console.In.ReadLine();
        }

        private static void TestMethod(Object x)
        {
            // This is a dummy value, just to get 'a' to be of the right type
            var a = new { Id = 0, Name = "" };
            a = Cast(a, x);
            Console.Out.WriteLine(a.Id + ": " + a.Name);
        }

        private static T Cast<T>(T typeHolder, Object x)
        {
            // typeHolder above is just for compiler magic
            // to infer the type to cast x to
            return (T)x;
        }
    }
}

The trick is that inside the assembly, the same anonymous type (same properties, same order) resolves to the same type, which makes the trick above work.

private static T CastTo<T>(this Object value, T targetType)
{
    // targetType above is just for compiler magic
    // to infer the type to cast value to
    return (T)value;
}

usage:

var value = x.CastTo(a);

But we're really pushing the limits here. Use a real type, it'll look and feel cleaner as well.

Darrel K.
  • 1,611
  • 18
  • 28
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • 1
    I don't like this though as it can be error prone, far better to just create an actual class to hold the values. – Chris Chilvers Sep 11 '09 at 08:51
  • I agree, though it's not really all that error prone, but I agree, a real type is warranted here, I edited the answer to clarify. – Lasse V. Karlsen Sep 11 '09 at 08:53
  • 7
    According to Mads Torgersen, the C# team refer to this trick as "cast by example". See his comment (the first) on this article: http://tomasp.net/blog/cannot-return-anonymous-type-from-method.aspx – LukeH Sep 11 '09 at 09:04
  • I've seen it referred to as "EvilCast" as well :) Just goes to show, although something *is* possible, doesn't mean we should do it. – Lasse V. Karlsen Sep 11 '09 at 09:08
  • 1
    That's a very clever trick, but as with almost all `clever` code, it's code that shouldn't be used. – Brett Ryan Sep 11 '09 at 09:54
  • 2
    Totally agree, here I would at the very least use a Tuple or similar predefined type in a framework, but I would probably create a new type for this scenario. – Lasse V. Karlsen Sep 11 '09 at 10:06
  • 5
    Everyone keeps saying "ooh, that's evil", but why? It is only runtime-checkable, but lots of languages work exclusively that way, like Python. That can be an acceptable design decision. Anyway, if you're casting from an object back into anything, it's only runtime-checkable anyway. So whats the difference if it's one string or two strings that you are casting back into? – Scott Stafford Apr 21 '10 at 15:07
  • 2
    I agree with the 'shouln't be used', and now there is Tuple<,...> if you're really too lazy to write a tiny class. – Guillaume86 Feb 15 '11 at 13:53
  • I am forced to use the anonymous method instead of strongly typed class for the following reason. I have to group a set of items based on month and year so I am grouping as new { x.date.Month, x.date.Year } which will group as expected. However, if I create a concrete class called MonthAndYear which has two ints Month and Year, and trying to group it as new MonthAndYear { Month = x.date.Month, Year = x.date.Year }, it groups each item into a separate bucket instead of putting items that belong to the same month & year into the same group. Unable to figure out how to get this working. – HelpMatters Oct 17 '14 at 18:43
  • Didn't work for me. I got this error `System.InvalidCastException: [A]<>f__AnonymousType0`2[System.String,System.String] cannot be cast to [B]<>f__AnonymousType0`2[System.String,System.String].` – Rod Talingting Jul 30 '20 at 10:34
19

Instead of casting to your custom type try using dynamic type.

Your event handler would look something like this:

private void cmdOK_Click(object sender, EventArgs e)
{
    dynamic option = bsOptions.Current;
    if (option.Id == 1) { doSomething(); }
      else { doSomethingElse(); }
}
Marko Juvančič
  • 5,792
  • 1
  • 25
  • 41
8

To quote MSDN:

An anonymous type cannot be cast to any interface or type except for object.

Vilx-
  • 104,512
  • 87
  • 279
  • 422
6

In C# 3.0, this is not possible. You'll have to wait for C# 4.0, which allows accessing properties at runtime using "dynamic" variables.

Philippe Leybaert
  • 168,566
  • 31
  • 210
  • 223
3
public class MyExtensMethods{

    public static T GetPropertyValue<T>(this Object obj, string property)
    {
        return (T)obj.GetType().GetProperty(property).GetValue(obj, null);
    }
}

class SomeClass
{
    public int ID{get;set;}
    public int FullName{get;set;}
}


// casts obj to type SomeClass
public SomeClass CastToSomeClass(object obj)
{
     return new SomeClass()
     {
         ID = obj.GetPropertyValue<int>("Id"),
         FullName = obj.GetPropertyValue<string>("LastName") + ", " + obj.GetPropertyValue<string>("FirstName")
     };
}

.... then to cast you will do:

var a = new { Id = 1, FirstName = "Bob", LastName="Nam" };
SomeClass myNewVar = CastToSomeClass(a);
Tono Nam
  • 34,064
  • 78
  • 298
  • 470
  • 1
    @gsharp. My use case is for Unit testing a Jsonresult, which is sending an anonymous type of multiple realized classes. So I already have SomeClasses but wanted an anonymous type for passing json. This works perfect for me thanks. – justin arsine Apr 18 '13 at 15:06
2

you can try this:

private void cmdOK_Click(object sender, EventArgs e)
{
    var option = Cast(bsOptions.Current, new { Id = 0, Option = "", Description = "" });
}

see: Can't return anonymous type from method? Really?

manji
  • 47,442
  • 5
  • 96
  • 103