Search code examples
c#dynamic.net-4.0datasourceanonymous-types

How to cast a object to an anonymous type in a different assembly?


I have a GridView where my DataSource is:

items.Select(i => new { ID = i.ID, Foo = i }).ToList();

In the RowDataBound I want to access the object, but I don't know how to cast it...

grid.RowDataBound += (s, e) =>
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        dynamic item = e.Row.DataItem as 'what?';
    }
};

How can I access this object properties?


Steps to reproduce

  1. Create a website
  2. Create a page (default.aspx) and place a <asp:GridView ID="grid" runat="server" />
  3. In the code behind:

add

protected void Page_Load(object sender, EventArgs ev)
{
    var provider = new FooProvider();

    grid.DataSource = provider.Elements;
    grid.RowDataBound += (s, e) =>
    {
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            dynamic item = e.Row.DataItem;

            var test = item.ID;
        }
    };
    grid.DataBind();
}
  1. Create a Class Library project
  2. Add a file FooProvider.cs

Code:

public class FooProvider
{
    public IEnumerable<dynamic> Elements
    {
        get
        {
            return new int[] { 1, 2, 3 }
                .Select(i => new { ID = i, Foo = i * 3 }).ToList();
        }
    }
}

Then add the reference on the website.


Expected result: Get the ID of the anonymous object.

Current result:

RuntimeBinderException was unhandled by user code

'object' does not contain a definition for 'ID'


Is reflection the only way?


Solution

  • If you're using dynamic you don't need to cast it at all. Just cast the values that you know the type of.

    dynamic item = e.Row.DataItem;
    DoSomething((int)item.Foo)
    

    (Though I agree with David B that I'd rather have a type safe, declared class.)

    And by the way, there is a trick you can use with generics which would allow you to do something like this:

    var item = e.Row.DataItem.CastAnonymous(new {ID = 1, Foo = 1});
    DoSomething(item.Foo);
    

    ... but I think that's the worst of the three options.

    Edit

    When working across assemblies, reflection is the only way to do what you're asking. Anonymous types were always intended to stay within the confines of a single method--that's why you can't declare them as a parameter or a return type. They make things like LINQ statements far less tedious, but their purpose is not to make C# into a scripting language. Is there a reason that you're dead-set against declaring a strong type?

    Edit 2

    Several years on now, we've got some lightweight type options available without using anonymous types.

    Records allow you to do a class declaration with a single line of code:

    public record FooElement(int ID, int Foo);
    
    public IEnumerable<FooElement> Elements
    {
        get
        {
            return new int[] { 1, 2, 3 }
                .Select(i => new FooElement(i, i * 3)).ToList();
        }
    }
    
    var item = (FooElement) e.Row.DataItem;
    var test = item.ID;
    

    And if you really don't want to create a named class to represent your return type, you can use ValueTuple syntax:

    public IEnumerable<(int ID, int Foo)> Elements
    {
        get
        {
            return new int[] { 1, 2, 3 }
                .Select(i => (i, i * 3)).ToList();
        }
    }
    
    var item = ((int ID, int Foo)) e.Row.DataItem;
    var test = item.ID;
    

    or

    var (test, _) = ((int, int)) e.Row.DataItem;