1/11/08

How to create a generic List of anonymous types?

There was a question on one of the forums asking how to create a list of anonymous types given a single instance of an anonymous type:

            var Customer = new { FirstName = "John", LastName = "Doe" };
var customerList = new List<????>();

My first reaction was to answer that this was impossible, and even if it was, for what purpose, but thankfully people replied before me, who knew better. I was amazed by these ingenious tricks. For example, Kael Rowan suggested:


            var Customer = new { FirstName = "John", LastName = "Doe" };
var customerList = (new[] { Customer }).ToList();

customerList.Add(new { FirstName = "Bill", LastName = "Smith" });

I think this is brilliant! This technique is called casting by example. Here's another implementation, using a "list factory":


        static void Main(string[] args)
{
var Customer = new { FirstName = "John", LastName = "Doe" };
var customerList = MakeList(Customer);

customerList.Add(new { FirstName = "Bill", LastName = "Smith" });
}

public static List<T> MakeList<T>(T itemOftype)
{
List<T> newList = new List<T>();
return newList;
}

They use generic type inference here to "name" the anonymous type to T and return a List of it.


This somewhat made me think of var as a black hole - once a type is being "converted" to a var, it can never come back explicitly (well, unless you want to cast :) It can reappear in another method as another "var" (casting by example) or be remanifested as inferred generic type parameter, but it can't get an explicit name ever again.


There was a discussion a while ago (between Wilco Bauwer, Rick Strahl and yours truly), whether C# should extend type inference to return types of methods, types of fields, properties etc. : Returning var from a method in C# 3.0 Generally it was agreed that this would not be a good thing to do, for various reasons. Eric Lippert gave a good summary of all the trouble it might bring:



  • what if languages consume the method, which don't support type inference?

  • how to store this in CLR metadata?

  • how to detect versioning problems once you update your class?

  • how to deal with two structurally identical types from different assemblies?

  • etc.


My personal feeling about this all (which sort of corresponds with the general consensus), is that one shouldn't use anonymous types for more than trivial LINQ operations. If a type is going to be reused, give it a proper name and declaration. Anonymous types don't scale (at least, not in C# 3.0).


If there is ever going to be a C# feature to use var as a return type of members, this is going to be the first time ever I'll want the C# team not to implement a feature :)

9 comments:

Nico said...

I was trying to do something similar and I manage to do it in this way:

var entities = new[] { new { EntityId = 1, Name = "Contoso" } };

I hope this help.

AutoRevit said...

var customerList = (new[] { Customer }).ToList();

not the same as

var customerList = MakeList(Customer);
public static List<T> MakeList<T>(T itemOftype)
{
List<T> newList = new List<T>();
return newList;
}

Isn't should be:
public static List<T> MakeList<T>(params T[] items)
{
return new List<T>(items);
}
and now you may use this:
var customerList = MakeList(Customer, new { FirstName = "Bill", LastName = "Smith" });

Convergence Training said...

I've used this as reference on several occasions, very helpful. Thanks for the post!

Anonymous said...
This comment has been removed by a blog administrator.
Vlad said...

Thanks, this was inspiring!

ToddR said...

once a type is being "converted" to a var, it can never come back explicitly

What about the following:
private class ExplicitCustomer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}

static void Main(string[] args)
{
var Customer = new { FirstName = "John", LastName = "Doe" };
var customerList = MakeList(Customer);

customerList.Add(new { FirstName = "Bill", LastName = "Smith" });

IEnumerable<ExplicitCustomer> customers = from cust in customerList
select new ExplicitCustomer()
{
FirstName = cust.FirstName,
LastName = cust.LastName
};
}


That gets you back into an explicit type. I guess you could say that's a form of casting, but it's a bit more elegant.

RedFury said...

Nope you are not casting, you are using LINQ to make shallow copies of the anonymous type.

You have created new objects which live their own live now.

But i like the method ;)

James praker said...

I have used many techniques while Web Designing and developing but found some new thoughts form your blog post!
Thanks for sharing this information!

sondlerd said...

wow, this is a great post. It's so convenient to be able to spin up a new type to captures some odd returned sql values without having to go through the trouble of creating a new class.

thanks!