Getting type of items of a List at run-time in c#

只谈情不闲聊 提交于 2019-12-13 00:11:38

问题


I need an expression that gets type of every item of a collection.

I have a collection like this:

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

It's possible to get type of every item of collection like this:

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

But I need creation of this expression at run-time. I have to create something like myVariable dynamically, but how?!

Debugger shows me value of internal expression of myVariable like this:

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

EDIT1

Explanation of problem: Assume that I need to select one property of an object. I cat write this query like this:

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

result is a list of type anonymous contains 2 items: "myName1"&"myName2". Now I want to add another property to resulted list that shows the type of selected property. The following code gets me thus list:

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

The problem is to create such expression at run-time to give the expression to entity framework and it executes the expression. Is the problem cleared?


回答1:


Given your recent edit, here's how to compile an expression such as the following at run-time:

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

I've added comments to show how the expression is being constructed. You can adapt this to your needs.

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

You'll need this method to create a new anonymous type:

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:


Objects like myVariable implement the generic IEnumerable<T> interface. Thus, you can retrieve the interface and get the element type as the generic parameter:

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

Edit: Or do you only need the types of the elements? You can achieve this with a cast to IEnumerable<object> since the type parameter of IEnumerable<T> is covariant.

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



回答3:


You just need to iterate the List to get the actual 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);
    }

EDIT:

The Type you're seeing in the debugger:

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

is the runtime type of the Linq query represented by myVariable. You need to iterate such a query in order to get the individual values. Cheers



来源:https://stackoverflow.com/questions/21878344/getting-type-of-items-of-a-list-at-run-time-in-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!