6

我有这个匿名类型:

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

我怎样才能使它成为一个数组Objects(每个元素->强制转换为对象)

因此类似于:

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

编辑

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

很抱歉没有提到它。

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

类型:System.Object[] 与此参数的数量、顺序 (!!!) 和类型(在默认绑定器的约束下)相匹配的值数组 ....

4

6 回答 6

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"
        }
    }
}
于 2012-06-26T17:10:05.450 回答
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));
}
于 2012-06-26T17:12:20.320 回答
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而不是将属性名称写入控制台来添加到数组。

于 2012-06-26T17:11:32.147 回答
0

如果您需要动态创建反射,则可以使用反射。如果它不需要是动态的,你显然可以这样做,但我假设你已经想到了这一点:

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

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

您能否就您的问题提供更深入的观点,因为您没有给我们太多继续,如果您不从匿名类型开始,也许有更好的解决方案?

于 2012-06-26T17:17:07.793 回答
0

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

var t = new { a = 1, b = "lalala", c = DateTime.Now };
object[] v = new object[] { t.a, t.b, t.c };
于 2012-06-26T17:14:00.867 回答
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();

如果您需要防止匿名类型中的对象在其中呈现的可能性,您也可以从myAnonToString方法代码 ( ) 中读取字符串常量,从而摆脱简单的解析器。myAnon.GetType().GetMethod("ToString").GetMethodBody()" = "

于 2012-06-26T17:43:38.377 回答