Místní funkce (C# Průvodce programováním)Local functions (C# Programming Guide)

Počínaje C# 7,0 C# podporuje místní funkce.Starting with C# 7.0, C# supports local functions. Lokální funkce jsou soukromé metody typu, které jsou vnořené v jiném členu.Local functions are private methods of a type that are nested in another member. Mohou být volány pouze ze svých nadřazených členů.They can only be called from their containing member. Místní funkce mohou být deklarovány v a volány z:Local functions can be declared in and called from:

  • Metody, zejména iterátorové metody a asynchronní metodyMethods, especially iterator methods and async methods
  • KonstruktoryConstructors
  • Přistupující objekty vlastnostiProperty accessors
  • Přístupové objekty událostíEvent accessors
  • Anonymní metodyAnonymous methods
  • Výrazy lambdaLambda expressions
  • Finalizační metodyFinalizers
  • Jiné místní funkceOther local functions

Místní funkce však nelze deklarovat uvnitř člena Expression-těle.However, local functions can't be declared inside an expression-bodied member.

Poznámka

V některých případech můžete použít výraz lambda pro implementaci funkcí, které podporuje také místní funkce.In some cases, you can use a lambda expression to implement functionality also supported by a local function. Porovnání naleznete v tématu místní funkce v porovnání s lambda výrazy.For a comparison, see Local functions compared to Lambda expressions.

Místní funkce usnadňují záměr vašeho kódu.Local functions make the intent of your code clear. Každý, kdo čte váš kód, uvidí, že metoda není možné volat s výjimkou obsahující metody.Anyone reading your code can see that the method is not callable except by the containing method. U týmových projektů také znemožňuje, aby jiný vývojář omylem volal metodu přímo z jiného místa ve třídě nebo struktuře.For team projects, they also make it impossible for another developer to mistakenly call the method directly from elsewhere in the class or struct.

Syntaxe lokální funkceLocal function syntax

Lokální funkce je definována jako vnořená metoda uvnitř nadřazeného člena.A local function is defined as a nested method inside a containing member. Jeho definice má následující syntaxi:Its definition has the following syntax:

<modifiers: async | unsafe> <return-type> <method-name> <parameter-list>

Místní funkce můžou používat modifikátory Async a unsafe .Local functions can use the async and unsafe modifiers.

Všimněte si, že všechny místní proměnné, které jsou definovány v nadřazeném členu, včetně jeho parametrů metody, jsou přístupné v místní funkci.Note that all local variables that are defined in the containing member, including its method parameters, are accessible in the local function.

Na rozdíl od definice metody nemůže definice lokální funkce obsahovat následující prvky:Unlike a method definition, a local function definition cannot include the following elements:

  • Modifikátor přístupu ke členuThe member access modifier. Vzhledem k tomu, že všechny místní funkce jsou soukromé, včetně modifikátoru přístupu private , jako je klíčové slovo, vygeneruje chybu kompilátoru CS0106, modifikátor Private není pro tuto položku platný.Because all local functions are private, including an access modifier, such as the private keyword, generates compiler error CS0106, "The modifier 'private' is not valid for this item."

  • Klíčové slovo static .The static keyword. static Zahrnutí klíčového slova generuje chybu kompilátoru CS0106, modifikátor "static" není pro tuto položku platný. "Including the static keyword generates compiler error CS0106, "The modifier 'static' is not valid for this item."

Kromě toho nelze atributy použít pro místní funkci nebo její parametry a parametry typu.In addition, attributes can't be applied to the local function or to its parameters and type parameters.

Následující příklad definuje místní funkci s názvem AppendPathSeparator , která je soukromá pro metodu s názvem: GetTextThe following example defines a local function named AppendPathSeparator that is private to a method named GetText:

using System;
using System.IO;

class Example
{
    static void Main()
    {
        string contents = GetText(@"C:\temp", "example.txt");
        Console.WriteLine("Contents of the file:\n" + contents);
    }
   
    private static string GetText(string path, string filename)
    {
         var sr = File.OpenText(AppendPathSeparator(path) + filename);
         var text = sr.ReadToEnd();
         return text;
         
         // Declare a local function.
         string AppendPathSeparator(string filepath)
         {
            if (! filepath.EndsWith(@"\"))
               filepath += @"\";

            return filepath;   
         }
    } 
}

Místní funkce a výjimkyLocal functions and exceptions

Jednou z užitečných funkcí lokálních funkcí je to, že může dojít k okamžitému povrchování výjimek.One of the useful features of local functions is that they can allow exceptions to surface immediately. U iterátorů metod jsou výjimky vyhodnoceny pouze v případě, že je vyhodnocena vrácená sekvence, a ne při načtení iterátoru.For method iterators, exceptions are surfaced only when the returned sequence is enumerated, and not when the iterator is retrieved. Pro asynchronní metody jsou při očekávaných úlohách pozorovány jakékoli výjimky vyvolané v asynchronní metodě.For async methods, any exceptions thrown in an async method are observed when the returned task is awaited.

Následující příklad definuje OddSequence metodu, která vytváří výčty lichých čísel mezi zadaným rozsahem.The following example defines an OddSequence method that enumerates odd numbers between a specified range. Protože předá metodě OddSequence Enumerator číslo větší než 100, ArgumentOutOfRangeExceptionvyvolá metoda.Because it passes a number greater than 100 to the OddSequence enumerator method, the method throws an ArgumentOutOfRangeException. Jak výstup z příkladu ukazuje, plochy výjimky pouze při iteraci čísel, a ne při načtení čítače výčtu.As the output from the example shows, the exception surfaces only when you iterate the numbers, and not when you retrieve the enumerator.

using System;
using System.Collections.Generic;

class Example
{
   static void Main()
   {
      IEnumerable<int> ienum = OddSequence(50, 110);
      Console.WriteLine("Retrieved enumerator...");
      
      foreach (var i in ienum)
      {
         Console.Write($"{i} ");
      }
   }

   public static IEnumerable<int> OddSequence(int start, int end)
   {
      if (start < 0 || start > 99)
         throw new ArgumentOutOfRangeException("start must be between 0 and 99.");
      if (end > 100)
         throw new ArgumentOutOfRangeException("end must be less than or equal to 100.");
      if (start >= end)
         throw new ArgumentException("start must be less than end.");
         
      for (int i = start; i <= end; i++)
      {
         if (i % 2 == 1)
            yield return i;
      }   
   }
}
// The example displays the following output:
//    Retrieved enumerator...
//    
//    Unhandled Exception: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
//    Parameter name: end must be less than or equal to 100.
//       at Sequence.<GetNumericRange>d__1.MoveNext() in Program.cs:line 23
//       at Example.Main() in Program.cs:line 43

Místo toho můžete vyvolat výjimku při provádění ověřování a před načtením iterátoru vrácením iterátoru z místní funkce, jak ukazuje následující příklad.Instead, you can throw an exception when performing validation and before retrieving the iterator by returning the iterator from a local function, as the following example shows.

using System;
using System.Collections.Generic;

class Example
{
   static void Main()
   {
      IEnumerable<int> ienum = OddSequence(50, 110);
      Console.WriteLine("Retrieved enumerator...");
      
      foreach (var i in ienum)
      {
         Console.Write($"{i} ");
      }
   }

   public static IEnumerable<int> OddSequence(int start, int end)
   {
      if (start < 0 || start > 99)
         throw new ArgumentOutOfRangeException("start must be between 0 and 99.");
      if (end > 100)
         throw new ArgumentOutOfRangeException("end must be less than or equal to 100.");
      if (start >= end)
         throw new ArgumentException("start must be less than end.");
         
      return GetOddSequenceEnumerator();
      
      IEnumerable<int> GetOddSequenceEnumerator()
      {
         for (int i = start; i <= end; i++)
         {
            if (i % 2 == 1)
               yield return i;
         }   
      }
   }
}
// The example displays the following output:
//    Unhandled Exception: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
//    Parameter name: end must be less than or equal to 100.
//       at Sequence.<GetNumericRange>d__1.MoveNext() in Program.cs:line 23
//       at Example.Main() in Program.cs:line 43

Místní funkce lze použít podobným způsobem pro zpracování výjimek mimo asynchronní operaci.Local functions can be used in a similar way to handle exceptions outside of the asynchronous operation. Výjimky vyvolané v asynchronní metodě obvykle vyžadují, abyste prozkoumali vnitřní výjimky AggregateException.Ordinarily, exceptions thrown in async method require that you examine the inner exceptions of an AggregateException. Místní funkce umožňují vašemu kódu selhání rychle a umožňují, aby vaše výjimka byla vyvolána a byla sledována synchronně.Local functions allow your code to fail fast and allow your exception to be both thrown and observed synchronously.

Následující příklad používá asynchronní metodu pojmenovanou GetMultipleAsync k pozastavení po zadaný počet sekund a vrátí hodnotu, která je náhodné násobek tohoto počtu sekund.The following example uses an asynchronous method named GetMultipleAsync to pause for a specified number of seconds and return a value that is a random multiple of that number of seconds. Maximální zpoždění je 5 sekund; ArgumentOutOfRangeException výsledkem je, že je hodnota větší než 5.The maximum delay is 5 seconds; an ArgumentOutOfRangeException results if the value is greater than 5. Jak ukazuje následující příklad, výjimka, která je vyvolána, když je předána GetMultipleAsync hodnota 6 metodě, je zabalena AggregateException do metody po GetMultipleAsync zahájení provádění.As the following example shows, the exception that is thrown when a value of 6 is passed to the GetMultipleAsync method is wrapped in an AggregateException after the GetMultipleAsync method begins execution.

using System;
using System.Threading.Tasks;

class Example
{
   static void Main()
   {
      int result = GetMultipleAsync(6).Result;
      Console.WriteLine($"The returned value is {result:N0}");
   }

   static async Task<int> GetMultipleAsync(int secondsDelay)
   {
      Console.WriteLine("Executing GetMultipleAsync...");
      if (secondsDelay < 0 || secondsDelay > 5)
         throw new ArgumentOutOfRangeException("secondsDelay cannot exceed 5.");
         
      await Task.Delay(secondsDelay * 1000);
      return secondsDelay * new Random().Next(2,10);
   } 
}
// The example displays the following output:
//    Executing GetMultipleAsync...
//
//    Unhandled Exception: System.AggregateException: 
//         One or more errors occurred. (Specified argument was out of the range of valid values.
//    Parameter name: secondsDelay cannot exceed 5.) ---> 
//         System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
//    Parameter name: secondsDelay cannot exceed 5.
//       at Example.<GetMultiple>d__1.MoveNext() in Program.cs:line 17
//       --- End of inner exception stack trace ---
//       at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
//       at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
//       at Example.Main() in C:\Users\ronpet\Documents\Visual Studio 2017\Projects\local-functions\async1\Program.cs:line 8

Stejně jako u metody iterátoru můžeme kód z tohoto příkladu Refaktorovat, aby bylo ověřování provedeno před voláním asynchronní metody.As we did with the method iterator, we can refactor the code from this example to perform the validation before calling the asynchronous method. Jak ukazuje výstup z následujícího příkladu, ArgumentOutOfRangeException není zabalen AggregateExceptiondo.As the output from the following example shows, the ArgumentOutOfRangeException is not wrapped in a AggregateException.

using System;
using System.Threading.Tasks;

class Example
{
   static void Main()
   {
      int result = GetMultiple(6).Result;
      Console.WriteLine($"The returned value is {result:N0}");
   }

   static Task<int> GetMultiple(int secondsDelay)
   {
      if (secondsDelay < 0 || secondsDelay > 5)
         throw new ArgumentOutOfRangeException("secondsDelay cannot exceed 5.");
         
      return GetValueAsync();
      
      async Task<int> GetValueAsync()
      {
         Console.WriteLine("Executing GetValueAsync...");
         await Task.Delay(secondsDelay * 1000);
         return secondsDelay * new Random().Next(2,10);
      }   
   } 
}
// The example displays the following output:
//    Unhandled Exception: System.ArgumentOutOfRangeException: 
//       Specified argument was out of the range of valid values.
//    Parameter name: secondsDelay cannot exceed 5.
//       at Example.GetMultiple(Int32 secondsDelay) in Program.cs:line 17
//       at Example.Main() in Program.cs:line 8


Viz také:See also