有趣的C#本地函数

Putting the Fun in C# Local Functions

究竟什么是局部函数? (What Are Local Functions Exactly?)

Local functions are private methods of a type that are nested in another member. They can only be called from their containing member. Local functions can be declared in and called from:

局部函数是嵌套在另一个成员中的一种类型的private方法。 只能从其包含成员中调用它们。 可以在以下位置声明和调用局部函数:

  • Methods, especially iterator methods and async methods

    方法,尤其是迭代器方法和异步方法
  • Constructors

    建设者
  • Property accessors

    财产访问者
  • Event accessors

    事件访问器
  • Anonymous methods

    匿名方法
  • Lambda expressions

    Lambda表达式
  • Finalizers

    终结者

As with most things, sometimes, it's easier to just show you what a local function looks like:

与大多数事情一样,有时,仅显示局部函数的外观更容易:

public static IEnumerable<Address> SanitizeAddresses(List<Address> addresses)  
{
      foreach(Address address in addresses) 
      {
           yield return Anonymize(address);
      }

      // This is a local function
      Address Anonymize(Address address) { ... }
}

使用本地函数清理注释 (Cleaning Up Comments with Local Functions)

Putting the Fun in C# Local Functions

One of the first use cases that comes to mind that functions can help alleviate is any pesky sanitation or business logic rules, particularly those around string manipulation, etc. If you've worked in enough business applications, you've undoubtably seen something terrible with some massive comment as to why it's being done:

想到的第一个可以帮助缓解功能的用例之一是任何令人讨厌的环境卫生或业务逻辑规则,尤其是关于字符串操作的规则。如果您在足够多的业务应用程序中工作,那么毫无疑问,您会发现可怕的事情发生了。关于为什么要这样做的大量评论:

public static User ProcessUser(User user)  
{
      // All names must conform to some crazy Dr. Seuss-eqsue rhyming scheme
      // along with every other character being placed by its closest numerically
      // shaped equivalent
      var seussifyExpression = new Regex("...");
      user.Name = seussifyExpression.Replace(user.Name, "...");
      user.Name = user.Name
                      .Replace(..., ...)
                      .Replace(..., ...)
                      .Replace(..., ...);

      // Other processes omitted for brevity

      return user;
}

As you can see here, we have a series of chained replacements, some relying on strings, and others relying on regular expressions, which can make a method pretty clunky, especially if there are multiple operations to perform. Now, this is where you can define a local function to encapsulate all this business logic to replace your crazy comment:

如您在这里看到的,我们有一系列链式替换,其中一些依赖于字符串,而另一些依赖于正则表达式,这会使方法变得笨拙,尤其是在要执行多个操作的情况下。 现在,您可以在此处定义一个本地函数来封装所有这些业务逻辑以替换您的疯狂注释:

public static User ProcessUser(User user)  
{
      SanitizeName(user)

      // Other processes omitted for brevity

      return user;

      void SanitizeName(User user)
      {
          var seussifyExpression = new Regex("...");
          user.Name = seussifyExpression.Replace(user.Name, "...");
          user.Name = user.Name
                          .Replace(..., ...)
                          .Replace(..., ...)
                          .Replace(..., ...);

          return user;
      }
}

You could easily name your local function whatever you like, even ApplyBusinessLogicNamingRules() and include any necessary comments for reasoning that you'd like within there (if you absolutely need to answer why you are doing something), but this should help the rest of your code tell you what it's doing without a comment explicitly writing it all out.

您可以轻松地随便命名您的本地函数,甚至可以ApplyBusinessLogicNamingRules()并在其中包括任何必要的注释,以说明您想要的推理(如果您绝对需要回答为什么要做某事),但这将对其余部分有所帮助您的代码会告诉您它在做什么,而无需添加注释即可将其全部写清楚。

用本地功能全读彩虹 (Going All Reading Rainbow with Local Functions)

Putting the Fun in C# Local Functions

If readability isn't the single most important thing about code, then it's damn close to the top.

如果可读性不是关于代码的最重要的事情,那么它就在顶部附近。

LINQ is another popular area that local functions can assist with, especially if you have to do any type of crazy filtering logic over a series of records. You can define a series of local functions that can cover each step of your filtering process (or any process really), and more easily reason about your code from a readability perspective:

LINQ是本地功能可以协助的另一个流行领域,尤其是如果您必须对一系列记录执行任何类型的疯狂筛选逻辑时。 您可以定义一系列本地函数,这些函数可以覆盖过滤过程(或实际上任何过程)的每个步骤,并且从可读性的角度更容易地为代码辩解:

public List<int> FindPrimesStartingWithASpecificLetter(List<int> numbers, int startingDigit)  
{
    return numbers.Where(n => n > 1 && Enumerable.Range(1, n).Where
                        (x => n % x == 0).SequenceEqual(new [] {1, n }))
                  .Where(n => $"{n}".StartsWith($"{startingDigit}"));
}  

While succinct, it doesn't exactly read well. Let's take a gander at what it looks like after rubbing some local functions on it:

虽然简洁,但阅读效果并不理想。 让我们来看看它在摩擦一些局部功能之后的样子:

public List<int> FindPrimesStartingWithASpecificLetter(List<int> numbers, int startingDigit)  
{
    return numbers.Where(x => IsPrime(x) && StartsWithDigit(x, startingDigit));

    bool IsPrime(int n) => return n > 1 && Enumerable.Range(1, n).
                           Where(x -> n % n == 0).SequenceEqual(new [] { 1, n }));
    bool StartsWithDigit(int n, int startingDigit) => return $"{n}".StartsWith
                        ($"{startingDigit}");
}  

As you can see, local functions are assisting with wrapping up all the ugly/nasty logic within their own tiny little functions. This is a really trivial case, but as you might imagine if you have lines-upon-lines of code that isn't touching anything outside of one method, it's likely a solid candidate for a local function.

如您所见,局部函数正在帮助将所有难看/讨厌的逻辑包装在它们自己的微小函数中。 这是一个非常琐碎的情况,但是正如您可能想像的那样,如果您的代码行没有碰到一种方法之外的任何内容,那么它很可能是局部函数的可靠候选者。

测试,测试,可笑! (Testing, Testing, Lo-ca-lly!)

Putting the Fun in C# Local Functions

If you've spent any amount of time writing tests, either unit or integration, you are probably familiar with the fabled 'Arrange-Act-Assert` pattern, which is used to separate each piece of functionality when testing a given piece of code as follows:

如果您花费了大量时间编写单元或集成测试,则您可能熟悉寓言中的“ Arrange-Act-Assert”模式,该模式用于在测试给定代码段时将每个功能分开,例如:如下:

  • Arrange all necessary preconditions and inputs.

    安排所有必要的前提条件和输入。

  • Act on the object or method under test.

    对被测对象或方法采取行动

  • Assert that the expected results have occurred.

    断言已经发生了预期的结果。

As you might imagine, this could lend itself to the pattern quite well for complex test cases:

正如您可能想象的那样,对于复杂的测试用例,这可能非常适合这种模式:

public void IsThisAnArrangeActAssertLocalFunction()  
{
     Arrange();
     Act();
     Assert();

     void Arrange() { ... }
     void Act() { ... }
     void Assert() { ... }
}

Is it practical? Does it fit all use cases? Is it something that you'd ever find yourself using? The answers to all of these might be an overwhelming no, but it does seem like a scenario where local functions could play a role.

实用吗? 它适合所有用例吗? 您会发现自己使用过的东西吗? 所有这些答案都可能是压倒性的,但是似乎本地功能可以发挥作用。

选择自己的冒险 (Choose Your Own Adventure)

Putting the Fun in C# Local Functions

Local functions present a few interesting options that fit some scenarios better than others. As a replacement for large comments or very messy business logic - absolutely. In unit tests or little one liners - probably not. With most new features, especially those that are sugary, it's really up to you and your team to see if they work for you. While they may seem appealing in some situations, they also seem ripe for abuse, potentially cluttered methods, and other issues that would completely defeat the purpose of using them in the first place.

局部函数提供了一些有趣的选项,这些选项比某些情况更适合某些情况。 绝对可以替代大型评论或非常混乱的业务逻辑。 在单元测试或很少的衬里中-可能没有。 拥有大多数新功能,尤其是那些含糖的功能,这取决于您和您的团队是否真正为您工作。 尽管它们在某些情况下似乎很有吸引力,但对于滥用,可能杂乱的方法以及其他将完全破坏首先使用它们的目的的问题,它们似乎也已经成熟。

So, if you choose to go down this road of local functions, proceed with care.

因此,如果您选择走这条当地职能之路,请谨慎行事。

翻译自: https://www.codeproject.com/Articles/5260938/Putting-the-Fun-in-Csharp-Local-Functions

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值