Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# “项目的任何方式”;原版加上一些改动”;用LINQ查询?_C#_Linq_Projection - Fatal编程技术网

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