使用反射创建lambda表达式,如x =>新的{..}

时间:2013-01-11 09:36:58

标签: c# reflection lambda expression

我有一个IQuerable<object> source对象,必须得到类似的东西(但使用反射)。

source.Select(t => new SelectListItem { Name = t.Name, Value = t.Id })

我该怎么做,或者在哪里可以找到构建那种表达式树的参考文献。

由于

2 个答案:

答案 0 :(得分:12)

您可以使用Expression命名空间(MSDN

创建System.Linq.Expressions

在你的情况下,它看起来像这样:

var source = typeof( Source );
var target = typeof( SelectListItem );

var t = Expression.Parameter( source, "t" );

var sourceName = Expression.MakeMemberAccess( t, source.GetProperty( "Name" ) );
var sourceId = Expression.MakeMemberAccess( t, source.GetProperty( "Id" ) );

var assignName = Expression.Bind( target.GetProperty( "Name" ), sourceName );
var assignValue = Expression.Bind( target.GetProperty( "Value" ), sourceId );
var targetNew = Expression.New( target );
var init = Expression.MemberInit( targetNew, assignName, assignValue );

var lambda =
  ( Expression<Func<Source,SelectListItem>> ) Expression.Lambda( init, t );

你可以像这样使用它:

IQueryable<Source> list = ...

List<SelectListItem> items = list.Select( lambda ).ToList();

答案 1 :(得分:0)

Its a little unclear as to what Type information (if any) is known during runtime. Piggybacking on Nicholas Butler's answer (since its been accepted) Let's assume that you will "know" the source type (i.e. what is the Type of T in the IQueryable source) and you will "know" the target Type (item Type returned from the IQueryable.Select extension method). When I say "know", I mean it is something that can be discovered at runtime without dynamics, reflection, late-binding, etc. Otherwise his solution will only work if the source and target types just happen to have properties with those matching names (i.e. "Name/Id" & "Name/Value"). Given this, there is a pretty simple solution without having to manually construct your lambda expression... Solution: First lets define these 2 types just so we know what we are dealing with. I only do this because I do not know what types you are using, so these are really placeholders for what you are actually using, so this is not needed for your solution, just for demo/example purposes: //this is whatever your source item type is (t) public class NameIdPair { public string Name { get; set; } public string Id { get; set; } } //this is whatever the SelectListItem type is you're using public class SelectListItem { public string Name { get; set; } public string Value { get; set; } } Next lets define a simple static class with 2 methods. One method will create the lambda expression and the other method will convert and select the source (IQueryable) into IEnumerable: public static class QueryableExtensions { public static IEnumerable<TItem> Select<TSource, TItem>(this IQueryable<TSource> source) where TSource : NameIdPair where TItem : SelectListItem, new() { if (source == null) throw new ArgumentNullException("source"); return source.Select(CreateLambda<TSource, TItem>()); } public static Expression<Func<TSource, TItem>> CreateLambda<TSource, TItem>() where TSource : NameIdPair where TItem : SelectListItem, new() { return (t) => new TItem { Name = t.Name, Value = t.Id }; } } Usage: //create an instance of an IQueryable<T> for demo purposes var source = new[] { new NameIdPair {Name = "test1_name", Id = "test1_Id"}, new NameIdPair {Name = "test2_name", Id = "test2_Id"} }.AsQueryable(); //you can call the "Select" extension method to select the queryable into an enum. var enumerable = source.Select<NameIdPair, SelectListItem>(); //'enumerable' is an IEnumerable<SelectListItem> instance //or if you just want the lambda expression... var lambda = QueryableExtensions.CreateLambda<NameIdPair, SelectListItem>(); //lambda.ToString() returns "t => new SelectListItem() {Name = t.Name, Value = t.Id}"; So there you go. Not sure if that's is what you are looking for, or if it fits your needs. Hopefully someone will find it useful.