【问题标题】:Odd return syntax statement奇数返回语法语句
【发布时间】:2018-01-01 13:55:11
【问题描述】:

我知道这可能听起来很奇怪,但我什至不知道如何在互联网上搜索这种语法,而且我也不确定究竟是什么意思。

所以我查看了一些 MoreLINQ 代码,然后我注意到了这个方法

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));

    return _(); IEnumerable<TSource> _()
    {
        var knownKeys = new HashSet<TKey>(comparer);
        foreach (var element in source)
        {
            if (knownKeys.Add(keySelector(element)))
                yield return element;
        }
    }
}

这个奇怪的返回语句是什么? return _();?

【问题讨论】:

  • 或者你的意思是:return _(); IEnumerable&lt;TSource&gt; _()
  • @Steve,我想知道 OP 是否指的是return _(); IEnumerable&lt;TSource&gt; _() 而不是yield return
  • 我认为他的意思是return _(); IEnumerable&lt;TSource&gt; _()。他可能会被它看起来的方式而不是实际的 return 语句弄糊涂。
  • @AkashKava OP 说有一个奇怪的返回声明。不幸的是,代码包含两个返回语句。因此,如果人们对他/她所指的内容感到困惑,这是可以理解的。
  • 编辑了问题,再次对造成的混乱表示歉意。

标签: c# .net c#-7.0


【解决方案1】:

这是支持本地函数的 C# 7.0....

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
       this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new 
           ArgumentNullException(nameof(source));
        if (keySelector == null) throw 
             new ArgumentNullException(nameof(keySelector));

        // This is basically executing _LocalFunction()
        return _LocalFunction(); 

        // This is a new inline method, 
        // return within this is only within scope of
        // this method
        IEnumerable<TSource> _LocalFunction()
        {
            var knownKeys = new HashSet<TKey>(comparer);
            foreach (var element in source)
            {
                if (knownKeys.Add(keySelector(element)))
                    yield return element;
            }
        }
    }

Func&lt;T&gt; 的当前 C#

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
       this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new 
           ArgumentNullException(nameof(source));
        if (keySelector == null) throw 
             new ArgumentNullException(nameof(keySelector));

        Func<IEnumerable<TSource>> func = () => {
            var knownKeys = new HashSet<TKey>(comparer);
            foreach (var element in source)
            {
                if (knownKeys.Add(keySelector(element)))
                    yield return element;
            }
       };

        // This is basically executing func
        return func(); 

    }

诀窍是,_()在使用后声明,完全没问题。

局部函数的实际使用

上面的例子只是演示了如何使用内联方法,但如果你只调用一次方法,那么它很可能是没有用的。

但是在上面的例子中,正如 PhoshiLuaan 在 cmets 中提到的,使用本地函数有一个优势。由于除非有人对其进行迭代,否则不会执行具有 yield return 的函数,因此在这种情况下,将执行本地函数之外的方法,并且即使没有人迭代该值,也会执行参数验证。

我们在方法中多次重复代码,让我们看看这个例子..

  public void ValidateCustomer(Customer customer){

      if( string.IsNullOrEmpty( customer.FirstName )){
           string error = "Firstname cannot be empty";
           customer.ValidationErrors.Add(error);
           ErrorLogger.Log(error);
           throw new ValidationError(error);
      }

      if( string.IsNullOrEmpty( customer.LastName )){
           string error = "Lastname cannot be empty";
           customer.ValidationErrors.Add(error);
           ErrorLogger.Log(error);
           throw new ValidationError(error);
      }

      ... on  and on... 
  }

我可以用...优化它

  public void ValidateCustomer(Customer customer){

      void _validate(string value, string error){
           if(!string.IsNullOrWhitespace(value)){

              // i can easily reference customer here
              customer.ValidationErrors.Add(error);

              ErrorLogger.Log(error);
              throw new ValidationError(error);                   
           }
      }

      _validate(customer.FirstName, "Firstname cannot be empty");
      _validate(customer.LastName, "Lastname cannot be empty");
      ... on  and on... 
  }

【讨论】:

  • @ZoharPeled 好吧.. 发布的代码确实显示了该功能的用途.. :)
  • @ColinM 的好处之一是匿名函数可以轻松地从其“主机”访问变量。
  • 你确定在 C# 中这实际上被称为匿名函数吗?它似乎有一个名字,即_AnonymousFunction 或只是_,而我希望真正的匿名函数类似于(x,y) =&gt; x+y。我将其称为本地函数,但我不习惯 C# 术语。
  • 明确地说,正如似乎没有人指出的那样,这段代码 sn-p 正在使用本地函数,因为它是一个 iterator (注意产量),并且所以懒惰地执行。如果没有本地函数,您将需要接受输入验证在第一次使用时发生,或者有一个只会被其他方法调用的方法。
  • @ColinM 发布的 kuksmen 示例实际上是最终实现的主要原因之一 - 当您使用 yield return 创建函数时,在实际枚举可枚举之前不会执行任何代码。这是不可取的,因为你想例如立即验证论点。在 C# 中执行此操作的唯一方法是将方法分为两种方法 - 一种使用 yield returns,另一种没有。内联方法允许您使用 inside 方法声明 yield,避免混乱和潜在的误用严格位于其父级内部且不可重用的方法。
【解决方案2】:

考虑一个更简单的例子

void Main()
{
    Console.WriteLine(Foo()); // Prints 5
}

public static int Foo()
{
    return _();

    // declare the body of _()
    int _()
    {
        return 5;
    }
}

_() 是在包含 return 语句的方法中声明的本地函数。

【讨论】:

  • 是的,我知道本地函数是格式欺骗了我......希望这不会成为标准。
  • 你的意思是函数声明从同一行开始吗?如果是这样,我同意,这太可怕了!
  • 是的,我就是这个意思。
  • 除了下划线的命名也很糟糕
  • @AkashKava:问题不在于它是否是合法的 C#,而是当这样格式化时,代码是否易于理解(因此易于维护和阅读)。个人喜好会起作用,但我倾向于同意 Stuart 的观点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-18
  • 2021-09-28
  • 1970-01-01
  • 2015-07-28
相关资源
最近更新 更多