【问题标题】:Getting type of items of a List at run-time in c#在c#中运行时获取列表项的类型
【发布时间】:2014-02-19 22:11:26
【问题描述】:

我需要一个表达式来获取集合中每个项目的类型。

我有一个这样的收藏:

var collection = new List<object>{1,2.2,"string",'c'};

可以像这样获取每个集合项的类型:

var myVariable= collection.Select(item => item.GetType())

但是我需要在运行时创建这个表达式。 我必须动态创建myVariable 之类的东西,但是如何?!

调试器向我显示myVariable 的内部表达式的值,如下所示:

{System.Linq.Enumerable+WhereSelectListIterator`2[System.Object,System.Type]}

EDIT1

问题说明: 假设我需要选择一个对象的一个​​属性。我猫这样写这个查询:

var lst = new List<MyClass>
        {
            new MyClass {Name = "myName1", Value = 1},
            new MyClass {Name = "myName2", Value = 2}
        };
        var oneProperty = lst.Select(item => new {SelectedProp=item.Name});

结果是一个匿名类型的列表,包含 2 项:“myName1”和“myName2”。 现在我想将另一个属性添加到显示所选属性类型的结果列表中。以下代码让我如此列出:

            var oneProperty = lst.Select(item => new {SelectedProp=item.Name,TypeOfProp=item.GetType() });

问题是在运行时创建这样的表达式以将表达式提供给实体框架并执行该表达式。问题解决了吗?

【问题讨论】:

  • 用伪代码向我们展示您想要实现的目标。
  • 鉴于建议的解决方案不能解决您的问题,请说明您要达到的目标!
  • 帖子编辑和问题解释

标签: c# linq runtime expression gettype


【解决方案1】:

鉴于您最近的编辑,以下是如何在运行时编译如下表达式:

item => new AnonymousType() { SelectProp = item.name, TypeOfProp = item.GetType() }

我添加了 cmets 来显示表达式是如何构造的。您可以根据自己的需要进行调整。

[Fact]
public void CreateExpression()
{
    Type argType = typeof (MyClass);
    string propertyName = "Name";

    ParameterExpression paramExpression = Expression.Parameter(argType, "item");

    //Create "item.Name" and "item.GetType()" expressions
    var propertyAccessExpression = Expression.Property(paramExpression, propertyName); 
    var getTypeExpression = Expression.Call(paramExpression, "GetType", Type.EmptyTypes);

    Type anonType = CreateAnonymousType<String, Type>("SelectedProp", "TypeOfProp");

    //"SelectProp = item.name"
    MemberBinding assignment1 = Expression.Bind(anonType.GetField("SelectedProp"), propertyAccessExpression);
    //"TypeOfProp = item.GetType()"
    MemberBinding assignment2 = Expression.Bind(anonType.GetField("TypeOfProp"), getTypeExpression);

    //"new AnonymousType()"
    var creationExpression = Expression.New(anonType.GetConstructor(Type.EmptyTypes));

    //"new AnonymousType() { SelectProp = item.name, TypeOfProp = item.GetType() }"
    var initialization = Expression.MemberInit(creationExpression, assignment1, assignment2);

    //"item => new AnonymousType() { SelectProp = item.name, TypeOfProp = item.GetType() }"
    Expression expression = Expression.Lambda(initialization, paramExpression);
}

你需要这个方法来创建一个新的匿名类型:

public static Type CreateAnonymousType<TFieldA, TFieldB>(string fieldNameA, string fieldNameB)
{
    AssemblyName dynamicAssemblyName = new AssemblyName("TempAssembly");
    AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
    ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("TempAssembly");

    TypeBuilder dynamicAnonymousType = dynamicModule.DefineType("AnonymousType", TypeAttributes.Public);

    dynamicAnonymousType.DefineField(fieldNameA, typeof(TFieldA), FieldAttributes.Public);
    dynamicAnonymousType.DefineField(fieldNameB, typeof(TFieldB), FieldAttributes.Public);

    return dynamicAnonymousType.CreateType();
}

【讨论】:

  • 非常感谢!这正是我需要的!最好的问候:)
【解决方案2】:

myVariable 这样的对象实现了通用的IEnumerable&lt;T&gt; 接口。因此,您可以检索接口并获取元素类型作为通用参数:

var type = myVariable.GetType();
foreach (var i in type.GetInterfaces())
{
    if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
    {
        return i.GetGenericTypeArguments()[0];
    }
}
// not a generic collection
return typeof(object);

编辑: 还是您只需要元素的类型?您可以通过转换为 IEnumerable&lt;object&gt; 来实现这一点,因为 IEnumerable&lt;T&gt; 的类型参数是协变的。

collection.Cast<object>().Select(o => o.GetType())

【讨论】:

  • 感谢 Georg,但我需要动态创建表达式!您的回答不会在运行时创建任何表达式。
  • 您可以使用从这段代码中检索到的类型,并使用适当的类型参数调用 Select 扩展方法
  • 我需要创建一个表达式,通过执行它来填充 myVariable。
【解决方案3】:

您只需要迭代 List 即可获得实际的 Type:

    var collection = new List<object> { 1, 2.2, "string", 'c' };
    var myVariable = collection.Select(item => item.GetType());

    foreach(var m in myVariable)
    {
        Console.WriteLine(m.FullName);
    }

编辑:

您在调试器中看到的类型:

{System.Linq.Enumerable+WhereSelectListIterator`2[System.Object,System.Type]}

是由 myVariable 表示的 Linq 查询的运行时类型。您需要迭代这样的查询以获取各个值。 干杯

【讨论】:

  • 这只是一个示例,表明在 foreach 循环中,您将能够获得您正在寻找的实际类型。
  • Mrlucmorin 谢谢您,您假设 myVariable 的值是由“Select”函数提供的。但问题是创建一个名为“Expression”的东西,它在运行时获得“Select”函数的结果。
  • @Merta 我不想变得粗鲁或任何事情,但你解释得越多,我理解的越少。请退后一步,试着考虑如何以我们可以理解的方式表达您的问题。显然没有人明白你想要做什么。
猜你喜欢
  • 1970-01-01
  • 2011-12-06
  • 2023-03-17
  • 2022-11-10
  • 2016-04-08
  • 2010-11-02
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
相关资源
最近更新 更多