问题
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