Royi Namir
Royi Namir

Reputation: 148524

Anonymous types to an array of objects?

I have this anonymous type :

var t= new {a=1,b="lalala",c=DateTime.Now};

How can I make it an array of Objects ( each element -> cast to object)

hence to something like :

object[]  v = new object[] {1,"lalala",DateTime.Now};

edit

p.s. this is just a knowledge question about learning to convert from 1 type to other. i know i can initialize an array of object from the beginning. but this is a learning question.

sorry for not mentioned it.

order Is important...why? cause ConstructorInfo.Invoke is accepting

Type: System.Object[] An array of values that matches the number, order (!!!) and type (under the constraints of the default binder) of the parameters for this ....

Upvotes: 6

Views: 8086

Answers (6)

Tim S.
Tim S.

Reputation: 56536

I think this is better than Jon Skeet's solution, since it relies on the result of ToString rather than on subtler details of how anonymous types are generated:

var myAnon = new { a = "hi", b185310 = "lo" };
var names = Regex.Matches(myAnon.ToString(), @"([a-zA-Z0-9]+) = ");
var objArray = names.Cast<Match>().Select(name => myAnon.GetType().GetProperty(name.Groups[1].ToString()).GetValue(myAnon, null)).ToArray();

You might also be able to read the string constants from myAnon's ToString method code (myAnon.GetType().GetMethod("ToString").GetMethodBody()) if you need to protect against the possibility of an object in the anonymous type being rendered with " = " in it, thus throwing off the simplistic parser.

Upvotes: 0

Lee
Lee

Reputation: 144136

public object[] ToPropertyArray(object o)
{
    return o.GetType.GetProperties()
        .Select(p => p.GetValue(o, null))
        .ToArray();
}

EDIT: It looks like you want to invoke a constructor of some type from an anonymous type. It looks like the only way this is possible is if the parameter names match the property names of the anonymous type:

public static T ConstructFromAnonymous<T>(object anon)
{
    //get constructors for type ordered by number of parameters
    var constructors = typeof(T).GetConstructors().OrderByDescending(c => c.GetParameters().Length);

    //get properties from anonymous object
    Dictionary<string, PropertyInfo> properties = anon.GetType()
        .GetProperties()
        .ToDictionary(p => p.Name);

    ConstructorInfo bestMatch = constructors.FirstOrDefault(ci => IsMatch(ci, properties));
    if (bestMatch != null)
    {
        var parameters = bestMatch.GetParameters();
        object[] args = parameters.Select(p => properties[p.Name].GetValue(anon, null)).ToArray();
        return (T)bestMatch.Invoke(args);
    }
    else throw new ArgumentException("Cannot construct type");
}

private static bool IsMatch(ConstructorInfo ci, Dictionary<string, PropertyInfo> properties)
{
    var parameters = ci.GetParameters();
    return parameters.All(p => properties.ContainsKey(p.Name) && p.ParameterType.IsAssignableFrom(properties[p.Name].PropertyType));
}

Upvotes: 4

Jon Skeet
Jon Skeet

Reputation: 1500425

You'd have to use reflection, basically. It shouldn't be too hard via Type.GetProperties, but I don't know of anything "built-in".

As leppie pointed out, the ordering isn't simple - you'd have to examine the order of the parameters, which would at least give you the order of all the types of the properties. If you only had different types, that would be fine.

If you don't care about the ordering, you can use:

var array = t.GetType()
             .GetProperties()
             .Select(p => p.GetValue(t, null))
             .ToArray();

EDIT: I've just thought of something which will actually fix it, but it's implementation specific. The C# compiler generates anonymous types using generic types. So new { A = 5, B = "foo" } will actually create an anonymous type like this:

class <>_Anon<TA, TB>
{
    internal <>_Anon(TA a, TB b)
}

so you can work out the property names in order based on the generic types of the generic properties, then fetch the properties in order from the concrete type. But it's ugly...

using System;
using System.Linq;
using System.Reflection;

class Test    
{
    // Note: this uses implementation details of anonymous
    // types, and is basically horrible.
    static object[] ConvertAnonymousType(object value)
    {
        // TODO: Validation that it's really an anonymous type
        Type type = value.GetType();
        var genericType = type.GetGenericTypeDefinition();
        var parameterTypes = genericType.GetConstructors()[0]
                                        .GetParameters()
                                        .Select(p => p.ParameterType)
                                        .ToList();
        var propertyNames = genericType.GetProperties()
                                       .OrderBy(p => parameterTypes.IndexOf(p.PropertyType))
                                       .Select(p => p.Name);

        return propertyNames.Select(name => type.GetProperty(name)
                                                .GetValue(value, null))
                            .ToArray();

    }

    static void Main()
    {
        var value = new { A = "a", Z = 10, C = "c" };
        var array = ConvertAnonymousType(value);
        foreach (var item in array)
        {
            Console.WriteLine(item); // "a", 10, "c"
        }
    }
}

Upvotes: 6

Steve Stokes
Steve Stokes

Reputation: 1230

Reflection is the way to go if you need it dynamically created. If it doesn't need to be dynamic you could obviously do it like this but I assume you've already thought of this:

var t = new { a = 1, b = "lalala", c = DateTime.Now };

object[] v = new object[] { t.a, t.b, t.c };

Could you provide a more in-depth perspective on your issue as you're not giving us much to go on, perhaps there is a better solution if you don't begin with an anon type?

Upvotes: 0

Douglas
Douglas

Reputation: 54877

If your anonymous type will always have the same properties which are known at compile-time, then you could use the obvious explicit approach:

var t = new { a = 1, b = "lalala", c = DateTime.Now };
object[] v = new object[] { t.a, t.b, t.c };

Upvotes: 0

Miserable Variable
Miserable Variable

Reputation: 28752

See http://blogs.msdn.com/b/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx:

static void Main()
{
    //Anonymous Type
    var anyType = new
    {
        IntID = 1,
        StringName = "Wriju"
    };

    Type t = anyType.GetType();
    PropertyInfo[] pi = t.GetProperties();
    foreach (PropertyInfo p in pi)
    {
        //Get the name of the prperty
        Console.WriteLine(p.Name);
    }

    //Using LINQ get all the details of Property
    var query = from p in t.GetProperties()
                select p;
    ObjectDumper.Write(query);
}

You should be able to add to array using GetValue instead of writing property name to console.

Upvotes: 1

Related Questions