views:

558

answers:

5

Consider this:

var me = new { FirstName = "John", LastName = "Smith" };

This is fine as we can then do this:

Console.WriteLine("{0} {1}", me.FirstName, me.LastName);

However we can't do this:

public T GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

because we don't know the type of T.

We could do this:

public object GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

but then we'd have to inspect the properties of the object using reflection in order to access them:

var p = new Prog();
object o = p.GetMe();
Type t = o.GetType();
foreach (var prop in t.GetProperties())
{
    Console.WriteLine(prop.Name + ": " + prop.GetValue(o, null));
}

However what about if we could name an anonymous type as we define it? Of course it would no longer be anonymous, however it would be more succinct and maintainable than a normal class definition.

Consider this:

public Person GetMe()
{
    return new public class Person { FirstName = "John", LastName = "Smith" };
}

The benefit being it would then be possible to return the result of a complicated Linq query from a method without having to define the class explicitly.

Consider this relatively complex Linq query:

List<int> list = new List<int>();
var query = from number in list
            select
                new
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };

Instead of defining a class like so:

public class MyNumbers
{
    public int Number { get; set; }
    public int Square { get; set; }
    public int Absolute { get; set; }
    public IEnumerable<int> Range { get; set; }
}

in order to return the query variable from a method we could instead just do this:

List<int> list = new List<int>();
return from number in list
            select new public class MyNumbers
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };
+9  A: 

Actually, there's a "hack" that you can do to get an anonymous type back from a method. Consider this:

public object MyMethod()
    {
        var myNewObject = new
        {
            stringProperty = "Hello, World!",
            intProperty = 1337,
            boolProperty = false
        };

        return myNewObject;
    }

    public T Cast<T>(object obj, T type)
    {
        return (T)obj;
    }

You can now do this:

var obj = MyMethod();
var myNewObj = Cast(obj, new { stringProperty = "", intProperty = 0, boolProperty = false });

The myNewObj will now be an object of the same Type as the anonymous type.

BFree
I hadn't seen this before. This is a pretty cool trick... although I don't imagine it's usually a good idea to use this
Max Schmeling
Nice trick though the anon type is still internal I believe so can't be used across assemblies.
Jonathan Parker
Yea, this is just a hack and I can't see myself using this in production code. You should ask yourself Jonathan though, if you're going to reference this from another Assembly, do you REALLY think anonymous types are what you want??
BFree
@BFree Well they're not anonymous hence the "". I guess a better name would be auto types or inline types.
Jonathan Parker
My god this is hideous but I love it :)Not that I can see any use for the trick, but it certainly is clever.
Denis Troller
Note that this hack would only work within the same assembly.
configurator
I feel like voting +1 for the sheer brilliance and -1 for the sheer uglyness.
Cameron MacFarland
+4  A: 

What you are describing (named anonymous types) are basically "tuple types".

I think they would be a nice addition to C#.

If I were designing such a feature for C#, I would expose it using syntax like this:

tuple<int x, int y>

so that you could do:

public tuple<int x, int y> GetStuff()
{
}

I would then change the definition of anonymous types, so that:

new { x = 2, y = 2}

had tuple<int x, int y> as it's type, rather than an anonymous type.

Getting this to work with the current CLR is a little tricky, because once you can name an anonymous type in public signatures you need to be able to unify them across separately compiled assemblies. It can be accomplished by embedding a "module constructor" inside any assembly that uses a tuple type. See this post for an example.

The only downside to that approach is that it doesn't respect the CLR's "lazy" model for type generation. That means that assemblies that use many distinct tuple types might experience slightly slower load types. A better approach would be to add support for tuple types directly to the CLR.

But, apart from changing the CLR, I think the module constructor approach is the best way of doing something like this.

Scott Wisniewski
.Net 4 is adding BCL support for tuples. E.g., http://weblogs.asp.net/podwysocki/archive/2008/11/16/functional-net-4-0-tuples-and-zip.aspx
Greg D
The tuple types in the BCL don't treat field names as part of the type identity like anonymous types do, so it's not quite the same thing.
Scott Wisniewski
I think not having support for tuples with named fields is a bad thing.How often do you program using names like "item1" and "item2" in your fields? Probably never, I'd guess
Scott Wisniewski
With my solution you can name your fields whatever you like. I prefer it over First, Second, Third tuple fields.
Jonathan Parker
You can do the same thing with what I wrote out. It's just that tuple<int x, int y> is much shorter than an entire class definition.
Scott Wisniewski
That would be a neat idea.
Meta-Knight
A: 

Could you create an Interface with the properties FirstName and LastName and use that?

Charles Graham
+3  A: 

IMHO the root problem is nothing to do with anonymous types, but that declaring a class is too verbose.

Option 1:

If you could declare a class like this:

public class MyClass
{ properties={ int Number, int Square, int Absolute, IEnumerable<int> Range } }

or some other similarly quick way (like the tuple example) then you wouldn't feel the need to do hacky things with anonymous types just to save some code.

When 'compiler as a service' arrives in C#5, hopefully they'll do a good job of integrating it and we'll be able to use metaprogramming to solve these kinds of problems cleanly. Party like it's 1958!

Option 2:

Alternatively, in C#4, you could just pass an anonymous type around as dynamic and avoid all the casting. Of course this opens you up to runtime errors if you rename a variable, etc.

Option 3:

If C# would implement generics in the same way as C++, then you could pass the anonymous type into a method, and so long as it had the right members, it would just compile. You'd get all the benefits of static type safety, and none of the downsides. Every time I have to type where T : ISomething in C# I get annoyed that they didn't do this!

Orion Edwards
+4  A: 

The language feature you need is:

public var GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

That is, var would be valid as a method return type, and the compiler would infer the actual type from whatever is returned. You would then have to do this at the call site:

var me = GetMe();

Any two anonymous types with members of the same type would be the same type, so if you wrote other functions returning the same pattern, they would have the same type. For any types A and B where B has a subset of the members of A, then A is assignment-compatible with B (B is like a base class of A). If you wrote:

public var GetMeFrom(var names)
{
    return new { FirstName = names["First"], LastName = names["Last"] };
}

The compiler would effectively define this as a generic method with two type parameters, T1 being the type of names and T2 being the type returned by the indexer on T1 that accepts a string. T1 would be constrained so that it must have an indexer that accepts a string. And at the call site you would just pass anything that had an indexer that accepted a string and returned any type you like, and that would determine the type of FirstName and LastName in the type returned by GetMeFrom.

So type inference would figure all this out for you, automatically capturing whatever type constraints are discoverable from the code.

Daniel Earwicker