匿名类型到一个对象数组?

时间:2012-06-26 17:08:36

标签: c# .net .net-4.0 anonymous-types

我有这种匿名类型:

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

如何将其设为Objects数组(每个元素 - >强制转换为对象)

因此类似于:

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

修改

P.S。这只是一个关于学习从1种类型转换为其他类型的知识问题。 我知道我可以从一开始就初始化一个对象数组。但这是一个学习问题。

抱歉没有提到它。

顺序很重要......为什么?因为ConstructorInfo.Invoke正在接受

  

类型:System.Object []与数字匹配的值数组,   订单(!!!)和类型(在默认绑定器的约束下)的   这个参数......

6 个答案:

答案 0 :(得分:6)

基本上你必须使用反射。通过Type.GetProperties不应该太难,但我不知道“内置”的任何内容。

正如leppie指出的那样,排序并不简单 - 您必须检查参数的顺序,这至少会为您提供所有类型属性的顺序。如果你只有不同的类型,那就没问题了。

如果您不关心订购,可以使用:

var array = t.GetType()
             .GetProperties()
             .Select(p => p.GetValue(t, null))
             .ToArray();
编辑:我刚刚想到了一些能够实际修复它的东西,但它是特定于实现的。 C#编译器使用泛型类型生成匿名类型。所以new { A = 5, B = "foo" }实际上会创建一个这样的匿名类型:

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

因此您可以根据泛型属性的泛型类型按顺序计算属性名称,然后从具体类型中按顺序获取属性。但它太丑了......

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"
        }
    }
}

答案 1 :(得分:4)

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

编辑:看起来你想从匿名类型调用某种类型的构造函数。看起来这是可能的唯一方法是参数名称是否与匿名类型的属性名称匹配:

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));
}

答案 2 :(得分:1)

请参阅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);
}

您应该能够使用GetValue添加到数组,而不是将属性名称写入控制台。

答案 3 :(得分:0)

如果你的匿名类型总是具有在编译时已知的相同属性,那么你可以使用明显的显式方法:

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

答案 4 :(得分:0)

如果您需要动态创建,那么反射就是您的选择。如果它不需要是动态的,你显然可以这样做,但我想你已经想到了这个:

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

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

您是否可以对您的问题提供更深入的观点,因为您没有给我们太多帮助,如果您不以anon类型开头,可能有更好的解决方案?

答案 5 :(得分:0)

我认为这比Jon Skeet的解决方案更好,因为它依赖于ToString的结果,而不是关于如何生成匿名类型的更细微的细节:

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();

如果您需要防止匿名类型中对象的可能性,您也可以从myAnon的{​​{1}}方法代码(ToString)中读取字符串常量正在使用myAnon.GetType().GetMethod("ToString").GetMethodBody()进行渲染,从而抛弃了简单的解析器。