Let's say we've got the following method:

public object Test()
{
    return new { A = "Test" };
}

Is there a chance of getting the value that is stored in A?

var b = Test(); //Any chance to cast this to the anonymous type?
有帮助吗?

解决方案

Note that returning anonymous types or Tuple<> from a method is a bad thing to do

But you asked a question about how to do it, not about "is it a good idea"...

By using dynamic or reflection...

dynamic b = Test();
string str = b.A;

Or by cheating:

public static object Test()
{
    return new { A = "Test" };
}

public static string GetA(object obj)
{
    // We create an anonymous type of the same type of the one in Test()
    // just to have its type.
    var x = new { A = string.Empty };

    // We pass it to Cast, that will take its T from the type of x
    // and that will return obj casted to the type of the anonymous
    // type
    x = Cast(x, obj);

    // Now in x we have obj, but strongly typed. So x.A is the value we
    // want
    return x.A;
}

public static T Cast<T>(T type, object obj) where T : class
{
    return (T)obj;
}

string str = GetA(Test());

In C# all the anonymous types with the same properties of the same type that are in the same assembly are merged together. So the new { A } of Test() and of GetA() are of the same type.

The Cast<T> is a useful trick to extract the type from an anonymous type. You pass as the first parameter your typed anonymous type (the parameter is used only to "activate" the generic T) and as the second parameter the object you want to cast. Similar tricks can be used to create collections of generic types, like

public static T MakeList<T>(T type)
{
    return new List<T>();
}

其他提示

//Any chance to cast this to the anonymous type?

Yes, you can use cast by example.

public static T CastByExample<T>(this object obj, T example) {
     return (T)obj;
}

Note that this only works if you're in the same assembly. Anonymous types have the same type if they are the same assembly, and the properties have the same names of the same type in the same order.

Then:

object b = Test();
var example = new { A = "example" };
var casted = b.CastByExample(example);
Console.WriteLine(casted.A);

Alternatively, you can use dynamic:

dynamic b = Test();
Console.WriteLine(b.A);

Or, use reflection:

object b = Test();
var property = b.GetType().GetProperty("A");
var value = property.GetValue(b);
Console.WriteLine(value);

Or, you could just do the right thing and make a nominal (i.e., non-anonymous) type.

Any chance to cast this to the anonymous type?

Although you can do that but it is highly unreliable. Because whenever you will change your creation of anonymous type your code will suddenly break on other places without a trace.

You can read all the downfalls of casting anonymous types in blog by Jon Skeet here. Also worth reading are the comments by Marc Gravel.

Example of breaking change as discussed in the above blog.

using System;

static class GrottyHacks
{
    internal static T Cast<T>(object target, T example)
    {
        return (T) target;
    }
}

class CheesecakeFactory
{
    static object CreateCheesecake()
    {
        return new { Fruit="Strawberry", Topping="Chocolate" };
    }

    static void Main()
    {
        object weaklyTyped = CreateCheesecake();
        var stronglyTyped = GrottyHacks.Cast(weaklyTyped,
            new { Fruit="", Topping="" });

        Console.WriteLine("Cheesecake: {0} ({1})",
            stronglyTyped.Fruit, stronglyTyped.Topping);            
    }
}

All good. Now what if you suddenly realise that you need to change CreateCheeseCake to something like this

static object CreateCheesecake()
    {
        return new { Fruit="Strawberry", Topping="Chocolate", Base = "Biscuit" };
    }

Then what will happen to your this line

var stronglyTyped = GrottyHacks.Cast(weaklyTyped,
            new { Fruit="", Topping="" });

it won't work anymore.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top