我的代码正在构建一个对象集合。我正在尝试减少拨打的.ToList()
次呼叫,因此我尽可能长时间地将其保留为IEnumerable<T>
。
除了需要设置为传递给调用方法的值的两个属性外,我几乎完成了它:
private IEnumerable<X> BuildCollection(int setMe){
IEnumerable<Y> fromService = CallService();
IEnumerable<X> mapped = Map(fromService);
IEnumerable<X> filteredBySomething = FilterBySomething(mapped);
IEnumerable<X> sorted = filteredBySomething
.OrderBy(x=>x.Property1)
.ThenBy(x=>x.Property2);
// Here's the problem: return "sorted", but with each "Property3"
// and "Property4" set to "setMe". I don't want to do:
var sortedList = sorted.ToList();
sortedList.ForEach(s=>
{
s.Property3 = setMe;
s.Property4 = setMe;
};
return sortedList;
}
如果可以在select
中使用某种通配符,那么我可以执行以下操作:
return from f in filteredBySomething
order by f.Property1, f.Property2
select new {
f.*,
f.Property3 = setMe,
f.Property4 = setMe
};
也就是说,我想回流已排序的对象,但将Property3和Property4设置为传入的值。
有优雅的方法吗?
P.S。我认为这不重要,但最终会将该集合作为viewmodel发送到ASP.NET视图。显然,在View获取之前可能必须调用.ToList()
,但我希望这是唯一的时间。
P.P.S。我应该说类型X
有大约30个属性!使用
select new {
x.Property1,
x.Property2,
Property3 = setMe,
Property4 = setme,
// ...
}
没有用,因为...
将是另外26个属性。
答案 0 :(得分:6)
而不是:
var sortedList = sorted.ToList();
sortedList.ForEach(s=>
{
s.Property3 = setMe;
s.Property4 = setMe;
};
这样做:
sorted = sorted.Select(x =>
{
x.Property3 = setMe;
x.Property4 = setMe;
return x;
});
但是,如果您不想修改对象,则可以执行以下操作:
sorted = sorted.Select(x => new X()
{
Property3 = setMe,
Property4 = setMe,
// set all other properties to what they were before
// example: Property1 = x.Property1
});
我不相信有比这两个更好的方法。
答案 1 :(得分:1)
除非您要投射的对象类型提供了复制构造函数并且是可变的,否则,没有优雅的方法来执行此操作。
如果定义了复制构造函数且对象是可变的,则可以这样做:
var updatedSorted = sorted.Select(x => new X(x)
{
Property3 = setMe,
Property4 = setMe,
});
然而,匿名对象没有可访问的拷贝构造函数,也不可变,因此您必须完成将值复制到自己身上的工作。但是在一些辅助函数的帮助下,使用一些反射和一些好的ol可以减少它的痛苦。 LINQ。幸运的是,对于匿名对象,虽然我们在编译时无法访问这些类型,但这并不意味着我们无法在运行时创建新实例。
public static class AnonExtensions
{
public static TSource SetValues<TSource, TValue>(
this TSource source,
Expression<Func<TSource, TValue>> setter)
{
var copierExpr = new Copier<TSource, TValue>().Rewrite(setter);
var copier = copierExpr.Compile();
return copier(source);
}
public static IEnumerable<TSource> UpdateValues<TSource, TValue>(
this IEnumerable<TSource> source,
Expression<Func<TSource, TValue>> setter)
{
var copierExpr = new Copier<TSource, TValue>().Rewrite(setter);
var copier = copierExpr.Compile();
return source.Select(copier);
}
public static IQueryable<TSource> UpdateValues<TSource, TValue>(
this IQueryable<TSource> source,
Expression<Func<TSource, TValue>> setter)
{
var copierExpr = new Copier<TSource, TValue>().Rewrite(setter);
return source.Select(copierExpr);
}
private class Copier<TSource, TValue> : ExpressionVisitor
{
private readonly ParameterExpression param =
Expression.Parameter(typeof(TSource));
public Expression<Func<TSource, TSource>> Rewrite(
Expression<Func<TSource, TValue>> setter)
{
var newExpr = new SubstitutionVisitor(
setter.Parameters.Single(), param).Visit(setter.Body);
var body = this.Visit(newExpr);
return Expression.Lambda<Func<TSource, TSource>>(body, param);
}
protected override Expression VisitNew(NewExpression node)
{
var type = typeof(TSource);
var ctor = type.GetConstructors().Single();
var arguments = new List<Expression>();
var members = new List<MemberInfo>();
var propMap = GetPropertyMap(node);
foreach (var prop in type.GetProperties())
{
Expression arg;
if (!propMap.TryGetValue(prop.Name, out arg))
arg = Expression.Property(param, prop);
arguments.Add(arg);
members.Add(prop);
}
return Expression.New(ctor, arguments, members);
}
private Dictionary<string, Expression> GetPropertyMap(
NewExpression node)
{
return node.Members.Zip(node.Arguments, (m, a) => new { m, a })
.ToDictionary(x => x.m.Name, x => x.a);
}
}
private class SubstitutionVisitor : ExpressionVisitor
{
private Expression oldValue, newValue;
public SubstitutionVisitor(Expression oldValue, Expression newValue)
{ this.oldValue = oldValue; this.newValue = newValue; }
public override Expression Visit(Expression node)
{
return node == oldValue ? newValue : base.Visit(node);
}
}
}
这将允许您这样做:
var updatedSorted = sorted.UpdateValues(x => new
{
Property3 = setMe, // the types here should match the corresponding
Property4 = setMe, // property types
});
答案 2 :(得分:0)
您可以在类中创建一个私有方法(MyClass是您的课程类):
private void PlaceValues(MyClass c, int SetMe)
{
PropertyDescriptorCollection col = TypeDescriptor.GetProperties(c);
foreach (PropertyDescriptor prop in col)
{
if (prop.DisplayName != "Property1" & prop.DisplayName != "Property2")
{
prop.SetValue(c, SetMe);
}
}
}
然后在你的BuildCollection方法中你的过滤后的东西:
private IEnumerable<X> BuildCollection(int setMe){
IEnumerable<Y> fromService = CallService();
IEnumerable<X> mapped = Map(fromService);
IEnumerable<X> filteredBySomething = FilterBySomething(mapped);
IEnumerable<X> sorted = filteredBySomething
.OrderBy(x=>x.Property1)
.ThenBy(x=>x.Property2);
// Here's the problem: return "sorted", but with each "Property3"
// and "Property4" set to "setMe". I don't want to do:
sorted.AsParallel().ForAll(x => PlaceValues(x, SetMe));
//Or without AsParallel(),using .ForEach() instead....
return sorted.ToList();
}
编辑:扩展方法怎么样,如下所示:
public static void SetValues<TInn>(this IEnumerable<TInn> col, int ValueToApply)where TInn:MyClass
{
PropertyDescriptorCollection pdCollection = TypeDescriptor.GetProperties(typeof(TInn));
foreach (var item in col)
{
foreach (PropertyDescriptor des in pdCollection)
{
if (des.DisplayName != "Property1" & des.DisplayName != "Property2")
{
des.SetValue(item, ValueToApply);
}
}
}
}
然后,不是我上面提到的,你这样做:
删除sorted.AsParallel().ForAll(x => PlaceValues(x, SetMe));
并放置sorted.SetValues(SetMe);
或者在扩展方法中放置一个params string [],这样你就可以告诉方法哪些属性不设置或设置依赖...
答案 3 :(得分:0)
这就是我最后的结果:
private IEnumerable<X> BuildCollection(int setMe){
IEnumerable<Y> fromService = CallService();
IEnumerable<X> mapped = Map(fromService);
IEnumerable<X> filteredBySomething = FilterBySomething(mapped);
IEnumerable<X> sorted = filteredBySomething
.OrderBy(x=>x.Property1)
.ThenBy(x=>x.Property2);
// The method already returns IEnumerable<X> - make it an iterator
foreach (var x in sorted)
{
x.Property3 = setMe;
x.Property4 = setMe;
yield return x;
}
}