简体   繁体   English

C#Lambda表达式-将委托结果添加到通用列表

[英]c# lambda expression - add delegate results to generic list

Question: I have just wrote my first code using c# lambda expressions. 问题:我刚刚使用c#lambda表达式编写了我的第一个代码。 It works, but I am not sure if this is the best way to do it. 它可以工作,但是我不确定这是否是最好的方法。 Any recommendations on a better way to do the lambda expression? 有什么建议可以更好地表达lambda吗? It seems odd to have numerous lines of code in the expression like I do below. 像我下面所做的那样,在表达式中包含很多代码行似乎很奇怪。

Background: I have a generic list of delegates. 背景:我有一个通用的代表列表。 Each delegate function returns an enum value indicating what happened in the function. 每个委托函数都返回一个枚举值,指示该函数中发生了什么。 Upon evaluation of the delegate, I need to add the enum to a List if it was not a specific enum value. 在评估委托后,如果不是特定的枚举值,则需要将枚举添加到列表中。

Disclaimer: Code here is very generic, the real code actually does stuff in the delegates to determine the return value! 免责声明:这里的代码非常通用,实际的代码实际上是在委托中进行填充以确定返回值!


class Class1
{
    public enum WhatHappened
    {
        ThingA,
        ThingB,
        Nothing
    }

    private delegate WhatHappened del();

    public static List<WhatHappened> DoStuff()
    {
        List<del> CheckValues = new List<del>();

        List<WhatHappened> returnValue = new List<WhatHappened> { };

        CheckValues.Add(delegate { return method1(); });
        CheckValues.Add(delegate { return method2(); });

        CheckValues.ForEach(x =>
        {
            WhatHappened wh = x();
            if (wh != WhatHappened.Nothing)
                returnValue.Add(wh);
        });

        return returnValue;

    }

    private static WhatHappened method1()
    {
        return WhatHappened.Nothing;
    }

    private static WhatHappened method2()
    {
        return WhatHappened.ThingA;
    }

}

Note: I originally had the lambda like adding all the items (see below), then removing the ones I didn't want (WhatHappened.Nothing). 注意:我最初有lambda,就像添加所有项目(请参阅下文),然后删除不需要的项目(WhatHappened.Nothing)。

CheckValues.ForEach(x => returnValue.Add(x()));

Okay, a few suggestions: 好的,有几点建议:

  • Don't call your delegate del . 不要叫你的委托del In this case, I'd use Func<WhatHappened> - but if you do want to declare your own delegate type, give it a more descriptive name, and obey the .NET naming conventions. 在这种情况下,我将使用Func<WhatHappened> -但是如果您确实想声明自己的委托类型,请给它一个更具描述性的名称,并遵守.NET命名约定。
  • Instead of using anonymous methods to add to CheckValues , you can just use: 除了使用匿名方法添加到CheckValues ,您还可以使用:

     CheckValues.Add(method1); CheckValues.Add(method2); 

    The compiler will convert the method groups into delegates. 编译器会将方法组转换为委托。

  • I'd recommend not using Pascal case for a local variable name to start with. 我建议不要使用Pascal大小写作为开头的局部变量名称。

  • Your collection initializer for returnValues isn't really doing anything for you - just call the List<T> constructor as normal, or use my code below which doesn't require a local variable to start with. 您的returnValues集合初始returnValues设定项实际上并没有为您做任何事情-只需正常调用List<T>构造函数,或使用下面的我的代码即可,而该代码不需要以本地变量开头。
  • If your list really only has two delegates in it, I'd just call them separately. 如果您的列表中确实只有两个代表,那么我将分别称呼他们。 It's a lot simpler. 这要简单得多。
  • Otherwise you can indeed use LINQ as Jared suggests, but I'd do it slightly differently: 否则,您确实可以按照Jared的建议使用LINQ,但是我会做些微的不同:

     return CheckValues.Select(x => x()) .Where(wh => wh != WhatHappened.Nothing) .ToList(); 

EDIT: As suggested, here's the full example. 编辑:按照建议,这是完整的示例。 It's not quite the same as Denis's though... I've made a couple of changes :) 不过,它与Denis不太一样...我做了一些更改:)

public static List<WhatHappened> DoStuff()
{
    var functions = new List<Func<WhatHappened>> { Method1, Method2 };

    return functions.Select(function => function())
                    .Where(result => result != WhatHappened.Nothing)
                    .ToList();
}

(I'm assuming that method1 and method2 have been renamed to fit the naming convention. Of course in real life I'm sure they'd have more useful names anyway...) (我假设对method1method2进行了重命名以符合命名约定。当然,在现实生活中,我肯定他们会使用更多有用的名称...)

You can go lambda all the way by chaining Select (map) and Where (filter) instead of multiple FOR loops and IF statements 通过链接Select(映射)和Where(过滤器),而不是多个FOR循环和IF语句,可以一路走来lambda

// get results from the list of functions
var results = CheckValues.Select(x => x());

// filter out only the relevant ones.
var returnValues = results.Where(x => x != WhatHappened.Nothing);

Basically, you should think more declaratively instead of imperatively when work ing with lambdas. 基本上,在处理lambda时,您应该更具声明性,而不是命令性 It'll help you write more elegant code. 它可以帮助您编写更精美的代码。

I would simply use Linq, but that's just me: 我只会使用Linq,但这就是我:

public static List<WhatHappened> DoStuff()
{
    List<del> CheckValues = new List<del>();

    List<WhatHappened> returnValue = new List<WhatHappened>();

    CheckValues.Add(method1);
    CheckValues.Add(method2);

    return CheckValues
               .Select(dlg => dlg())
               .Where( res => res != WhatHappened.Nothing)
               .ToList();
}

Note that you can also use Func instead of declaring a Delegate type if you want, but that's less terse in that case. 请注意,如果需要,也可以使用Func而不是声明Delegate类型,但是在这种情况下,它不那么简洁。 Also, I'd return an IEnumerable<WhatHappened> instead of a List, but it's all about the context. 另外,我将返回IEnumerable<WhatHappened>而不是List,但这全都与上下文有关。

It's a bit more idiomatic to write the following instead of using the delegate keyword. 编写以下代码而不是使用委托关键字,这有点习惯。 It doesn't change the underlying functionality though. 它不会改变底层功能。

CheckValues.Add( () => method1() );

Also, I find it more readable to rewrite the ForEach as the following 另外,我发现重写ForEach更具可读性,如下所示

CheckValues = CheckValues.
  Select(x => x()).
  Where(wh => wh != WhatHappened.Nothing ). 
  ToList();

In my opinion, based on the example, it looks fine. 我认为,基于该示例,它看起来不错。 You could refactor even more by replacing: 您可以通过替换以下内容来进一步重构:

CheckValues.Add(delegate { return method1(); });
CheckValues.Add(delegate { return method2(); });

with: 与:

CheckValues.Add(() => WhatHappened.Nothing);
CheckValues.Add(() => WhatHappened.ThingA);

Here's a LINQ-free solution: 这是不使用LINQ的解决方案:

return CheckValues
    .ConvertAll<WhatHappened>(x => x())
    .FindAll(y => y != WhatHappened.Nothing);

caveat 警告

This is not the most performant solution, as it would iterate twice. 这不是性能最高的解决方案,因为它会迭代两次。

I can't fathom the purpose of the code.. however here goes. 我无法理解代码的目的..但是这里。
Used delegate chaining Update: and picked up some Enumerable goodness from Jon n Jared's posts 二手委托链接更新:并从乔恩·贾里德(Jon n Jared)的帖子中获取了一些可贵的优点

private delegate WhatHappened WhatHappenedDelegate();

public static List<WhatHappened> DoStuff()
{
    WhatHappenedDelegate delegateChain = null;
    delegateChain += method1;
    delegateChain += method2;

    return delegateChain.GetInvocationList() 
            .Select(x => (WhatHappened) x.DynamicInvoke())
            .Where( wh => (wh != WhatHappened.Nothing))
            .ToList<WhatHappened>();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM