【问题标题】:c# lambda expression - add delegate results to generic listc# lambda 表达式 - 将委托结果添加到通用列表
【发布时间】:2010-10-21 10:36:06
【问题描述】:

问题:我刚刚使用 c# lambda 表达式编写了我的第一个代码。它有效,但我不确定这是否是最好的方法。关于更好地执行 lambda 表达式的任何建议?像下面这样在表达式中有很多行代码似乎很奇怪。

背景:我有一个通用的代表名单。每个委托函数都返回一个枚举值,指示函数中发生了什么。在评估委托后,如果它不是特定的枚举值,我需要将枚举添加到列表中。

免责声明:这里的代码非常通用,真正的代码实际上是在委托中做一些事情来确定返回值!


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

}

注意:我最初有 lambda,比如添加所有项目(见下文),然后删除我不想要的项目(WhatHappened.Nothing)。

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

【问题讨论】:

    标签: c# .net .net-3.5 lambda


    【解决方案1】:

    在我看来,根据示例,它看起来不错。您可以通过替换进行更多重构:

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

    与:

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

    【讨论】:

      【解决方案2】:

      您可以通过链接 Select (map) 和 Where (filter) 而不是多个 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);
      

      基本上,在使用 lambda 时,您应该多考虑 declaratively 而不是 imperatively。它将帮助您编写更优雅的代码。

      【讨论】:

        【解决方案3】:

        写下面的代码比使用delegate关键字更习惯一些。但它不会改变底层功能。

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

        另外,我发现将 ForEach 重写为以下内容更具可读性

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

        【讨论】:

        • 除非你有 ForEach 扩展方法,否则它不会编译。我也喜欢在行首加上点:)
        • @Jon,我已经习惯了我的自定义 LINQ 方法,以至于我忘记了默认情况下它们不存在。更新以避免我不拥有的扩展方法。老实说,我在 dot before 或 dot after 上来回走动。我的正常偏好是dot before。但是我使用的某些语言需要字符在(PowerShell 和 VB.Net)之后,所以我来回漂移;)
        【解决方案4】:

        好的,有几个建议:

        • 请勿致电您的代表del。在这种情况下,我会使用 Func&lt;WhatHappened&gt; - 但如果您确实想要声明自己的委托类型,请给它一个更具描述性的名称,并遵守 .NET 命名约定。
        • 不要使用匿名方法添加到CheckValues,您可以使用:

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

          编译器会将方法组转换为委托。

        • 我建议不要使用 Pascal 大小写作为局部变量名的开头。

        • returnValues 的集合初始化程序并没有真正为您做任何事情 - 只需像往常一样调用 List&lt;T&gt; 构造函数,或者使用我下面的代码,它不需要以局部变量开头。
        • 如果您的列表真的只有两个代表,我会单独调用它们。这要简单得多。
        • 否则,您确实可以按照 Jared 的建议使用 LINQ,但我会稍有不同:

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

        编辑:按照建议,这是完整的示例。不过它和丹尼斯的不太一样……我做了一些改变:)

        public static List<WhatHappened> DoStuff()
        {
            var functions = new List<Func<WhatHappened>> { Method1, Method2 };
        
            return functions.Select(function => function())
                            .Where(result => result != WhatHappened.Nothing)
                            .ToList();
        }
        

        (我假设 method1method2 已被重命名以符合命名约定。当然在现实生活中我确信它们会有更多有用的名称......)

        【讨论】:

        • 我同意你的建议,但我认为你举的例子有点过于字面意思了。在示例代码中,我没有花时间研究命名约定。真正的代码有更多的描述性名称。
        • 如果您举一个您的代码示例,并询问我们会采取哪些不同的做法,我们如何知道您确实知道的错误与您所犯的错误之间的区别知道吗?
        • 我认为主要是使用现有的 Func 而不是您自己的委托类型。那么命名就没有实际意义了。
        • 该死的乔恩,你打败了我,你设法给出了更好的解释。你的键盘能读懂你的想法吗? :)
        • 这个答案中有很多很好的建议。我建议将它与 Denis Troller 的回答中完全重写的示例结合起来。
        【解决方案5】:

        我会简单地使用 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();
        }
        

        请注意,如果需要,您也可以使用 Func 而不是声明 Delegate 类型,但在这种情况下就不那么简洁了。 另外,我会返回一个IEnumerable&lt;WhatHappened&gt; 而不是一个列表,但这完全取决于上下文。

        【讨论】:

          【解决方案6】:

          这是一个无 LINQ 的解决方案:

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

          警告

          这不是最高效的解决方案,因为它会迭代两次。

          【讨论】:

            【解决方案7】:

            我无法理解代码的用途.. 但是这里有。
            使用委托链 更新: 并从 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>();
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-06-19
              • 1970-01-01
              • 1970-01-01
              • 2012-08-27
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多