17

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)
                    };
Jonathan Parker
  • 6,705
  • 3
  • 43
  • 54
  • 3
    That would get very ugly when you started using MyNumbers in other contexts. If you are using a type across method boundaries it is much more maintainable and readable if that type is not declared inline as you propose. – Eric Schoonover Mar 17 '09 at 02:29
  • Fair point though I think you could put some smarts into the compiler to mitigate these issues. – Jonathan Parker Mar 17 '09 at 02:34
  • I think the key to making it not ugly is to have seperate syntax for naming the type, vs the syntax for creating a value. See my post for an example. – Scott Wisniewski Mar 17 '09 at 02:55
  • Honestly I was nearly shitting myself when reading the title line. Even after seriously thinking about it, I cannot see great benefit. – codymanix Aug 18 '11 at 15:11

7 Answers7

14

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
  • 102,548
  • 21
  • 159
  • 201
9

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
  • 114,894
  • 38
  • 205
  • 284
5

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
  • 121,657
  • 64
  • 239
  • 328
  • A lot of the time it's useful to bind a bunch of variables together with duct tape. My preference would be to use a public-field value type for that purpose, and a simple generic `ExposedFieldHolder` or `ImmutableHolder` if mutable or immutable reference semantics are required. In some ways, I wish .NET had included two kinds of value types--those which were designed to behave like objects, and those which were designed to bind variables together with duct tape. If a type like `StructTuple{public T1 v1; public T2 v2;}` could be declared so as to be truly immutable when boxed... – supercat Jun 16 '13 at 02:07
  • ...then a `StructTuple` could be regarded as a subtype of a `StructTuple`. As it is, the fact that *all boxed structs are always mutable* makes structure-type covariance impossible even in cases where it would otherwise make sense. – supercat Jun 16 '13 at 02:09
4

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.

Community
  • 1
  • 1
Scott Wisniewski
  • 24,561
  • 8
  • 60
  • 89
  • .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 Mar 17 '09 at 02:58
  • 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 Mar 17 '09 at 03:03
  • 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 Mar 17 '09 at 03:03
  • With my solution you can name your fields whatever you like. I prefer it over First, Second, Third tuple fields. – Jonathan Parker Mar 17 '09 at 03:06
  • You can do the same thing with what I wrote out. It's just that tuple is much shorter than an entire class definition. – Scott Wisniewski Mar 17 '09 at 03:26
1

I would love this feature, there have been many times I've wanted this.

A good example is processing XML. You parse them get back an object, but then you need to make a concrete version of the object to send back to a caller. Many times you get XML that changes quite considerably and requires you make many classes to handle it. Wouldn't it be wonderful if you could just build the object using LinqToXml as a var, then just return that?

Kelly
  • 6,992
  • 12
  • 59
  • 76
0

I think this would be a nice compiler magic for tuples:

Creating a tuple:

(int, string, Person) tuple = (8, "hello", new Person());

equivalent to:

Tuple<int,string,Person> tuple = new Tuple<int,string,Person>(8 ,"hello", new Person());

In a function:

public (int, string, Person) GetTuple(){
    return ...
}

Getting values:

int number = tuple[1];
string text = tuple[2];
Person person = tuple[3];
Samantha Branham
  • 7,350
  • 2
  • 32
  • 44
Masterile
  • 143
  • 2
  • 7
-1

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

Charles Graham
  • 24,293
  • 14
  • 43
  • 56