C# “项目的任何方式”;原版加上一些改动”;用LINQ查询?
我有一些代码正在构建一个对象集合。我试图减少我所做的C# “项目的任何方式”;原版加上一些改动”;用LINQ查询?,c#,linq,projection,C#,Linq,Projection,我有一些代码正在构建一个对象集合。我试图减少我所做的.ToList()调用的数量,因此我试图尽可能长时间地将其保持为IEnumerable 除了两个属性需要设置为传递给调用方法的值之外,我几乎完成了这项工作: private IEnumerable<X> BuildCollection(int setMe){ IEnumerable<Y> fromService = CallService(); IEnumerable<X> mapped =
.ToList()
调用的数量,因此我试图尽可能长时间地将其保持为IEnumerable
除了两个属性需要设置为传递给调用方法的值之外,我几乎完成了这项工作:
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;
}
也就是说,我希望流式返回已排序的对象,但将Property3和Property4设置为传入的值
有没有一种优雅的方法可以做到这一点
另外,我认为这无关紧要,但集合最终将作为viewmodel发送到ASP.NET视图。显然,.ToList()
可能必须在视图获取它之前调用,但我希望这是唯一一次
p.p.S.我应该说类型X
大约有30个属性!使用
select new {
x.Property1,
x.Property2,
Property3 = setMe,
Property4 = setme,
// ...
}
不会有用,因为…
将是另外26个属性。而不是:
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
});
我认为没有比这两种方法更好的方法了。您可以在类中创建这样的私有方法(MyClass当然是您的类): 然后在BuildCollection方法中,使用filteredbysomething:
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();
}
private IEnumerable BuildCollection(int setMe){
IEnumerable fromService=CallService();
IEnumerable mapped=映射(来自服务);
IEnumerable filteredBySomething=FilterBySomething(映射);
IEnumerable sorted=filteredBySomething
.OrderBy(x=>x.Property1)
.ThenBy(x=>x.Property2);
//问题是:返回“sorted”,但每个“Property3”
//“Property4”设置为“setMe”。我不想:
sorted.AsParallel().ForAll(x=>PlaceValues(x,SetMe));
//或者不使用AsParallel(),而是使用.ForEach()。。。。
返回已排序的.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);
}
}
}
}
publicstaticvoidsetvalues(此IEnumerable列,int-ValueToApply),其中TInn:MyClass
{
PropertyDescriptorCollection pCollection=TypeDescriptor.GetProperties(typeof(TInn));
foreach(列中的var项)
{
foreach(pCollection中的PropertyDescriptor des)
{
如果(des.DisplayName!=“Property1”&des.DisplayName!=“Property2”)
{
des.设置值(项目、应用值);
}
}
}
}
然后,你可以这样做,而不是像我上面建议的那样:
移除sorted.AsParallel().ForAll(x=>PlaceValues(x,SetMe))代码>
并放置sorted.SetValues(SetMe)代码>
或者在扩展方法中放置一个params字符串[],以便告诉该方法不设置或设置哪些属性取决于…,除非要投影到的对象类型提供了一个副本构造函数并且是可变的,否则不,没有优雅的方法可以做到这一点
如果定义了复制构造函数且对象是可变的,则可以执行以下操作:
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);
}
}
}
以下是我的结论:
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;
}
}
private IEnumerable BuildCollection(int setMe){
IEnumerable fromService=CallService();
IEnumerable mapped=映射(来自服务);
IEnumerable filteredBySomething=FilterBySomething(映射);
IEnumerable sorted=filteredBySomething
.OrderBy(x=>x.Property1)
.ThenBy(x=>x.Property2);
//该方法已返回IEnumerable-使其成为迭代器
foreach(排序中的变量x)
{
x、 Property3=setMe;
x、 Property4=setMe;
收益率x;
}
}
这确实有变异状态的因素,在中应该是一个纯函数。选择@KirkWoll那么唯一的解决方案是在Select
中创建新的X
并将属性3
和属性4
设置为之前的状态。我将改变我的答案来说明这一点。对,但这个解决方案不正是约翰·桑德斯试图避免的吗?即*
愿望?我认为约翰问题的答案是,没有优雅的方式来做他想做的事。为什么要参照MyClass c
?您不能在PlaceValues
的内部将其分配给c
。也就是说,这里没有c=newmyclass()
,所以您不需要ref
。2.引入并行性并不是我所说的优雅。3.根据您的评论,如果我不使用AsParallel,那么我应该使用ForEach,这正是我想要避免的。OPSS是的,我不好,我以前尝试过其他选项(关于ref),关于其余的,我很抱歉john,这只是另一种选择…除了ForEach在我头顶上只调用jon skeet:)这将导致多重枚举。一次设置值,然后再次设置ToList。
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
});
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;
}
}