Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Local Functions trong C# 7.0

Local functions là private methods của type được nested trong member khác. Chúng chỉ có thể được gọi từ containing member của chúng.

Local functions có thể khai báo trong và gọi từ:

  • Methods, đặc biệt iterator methods và async methods
  • Constructors
  • Property accessors
  • Event accessors
  • Anonymous methods
  • Lambda expressions
  • Finalizers
  • Other local functions

Ví dụ 1 - Basic

class Program { 
    public static void Main() { 
        void AddTwoNumbers(int a, int b) { 
            System.Console.WriteLine(a + b); 
        } 
        AddTwoNumbers(1, 2); 
        Console.ReadLine(); 
    } 
}

Output

3

Ví dụ 2 - Out Parameter

class Program { 
    public static void Main() { 
        void AddTwoNumbers(int a, int b, out int c) { 
            c = a + b; 
        } 
        AddTwoNumbers(1, 2, out int c); 
        System.Console.WriteLine(c); 
        Console.ReadLine(); 
    } 
}

Output

3

Tại sao dùng Local Functions?

So với Ưu điểm Local Functions
Private Methods Chỉ visible trong containing method, encapsulation tốt hơn
Lambda/Delegates Không allocate delegate object, performance tốt hơn
Lambda/Delegates Hỗ trợ ref/out parameters, generic types
Lambda/Delegates Có thể recursive (gọi chính nó)
Lambda/Delegates Debugging tốt hơn (có tên trong stack trace)
Iterator Methods Eager validation trước khi yield return
Async Methods Eager validation trước khi await

Advanced Examples

1. Iterator Method - Eager Validation

public IEnumerable GetNumbers(int start, int count) {
    // Validation chạy NGAY LẬP TỨC khi gọi method
    if (count < 0) throw new ArgumentException("Count must be positive");
    if (start < 0) throw new ArgumentException("Start must be positive");
    
    // Iterator logic trong local function
    return GetNumbersImpl();
    
    IEnumerable GetNumbersImpl() {
        for (int i = 0; i < count; i++) {
            yield return start + i;
        }
    }
}

// Không dùng local function: validation chỉ chạy khi enumerate (lazy)
public IEnumerable GetNumbersBad(int start, int count) {
    if (count < 0) throw new ArgumentException("Count must be positive");
    for (int i = 0; i < count; i++) {
        yield return start + i; // Validation delayed!
    }
}

2. Async Method - Eager Validation

public Task DownloadAsync(string url) {
    // Validate ngay lập tức
    if (string.IsNullOrWhiteSpace(url)) 
        throw new ArgumentException("URL required");
    if (!Uri.TryCreate(url, UriKind.Absolute, out _)) 
        throw new ArgumentException("Invalid URL");
    
    return DownloadImplAsync(url);
    
    async Task DownloadImplAsync(string url) {
        using var client = new HttpClient();
        return await client.GetStringAsync(url);
    }
}

3. Recursive Local Function

public int Factorial(int n) {
    int FactorialImpl(int x) {
        return x <= 1 ? 1 : x * FactorialImpl(x - 1);
    }
    
    if (n < 0) throw new ArgumentException("n must be >= 0");
    return FactorialImpl(n);
}

4. Local Function với Closure (Capture Variables)

public int ProcessData(int[] data, int threshold) {
    int count = 0;
    int sum = 0;
    
    void ProcessItem(int item) {
        // Capture count, sum từ outer scope
        if (item > threshold) {
            count++;
            sum += item;
        }
    }
    
    foreach (var item in data) {
        ProcessItem(item);
    }
    
    return sum / count; // Average of items > threshold
}

5. Local Function trong Property/Constructor

public class Person {
    private string _firstName;
    private string _lastName;
    
    public Person(string fullName) {
        void ParseName(string name) {
            var parts = name.Split(' ', StringSplitOptions.RemoveEmptyEntries);
            _firstName = parts[0];
            _lastName = parts.Length > 1 ? parts[1] : "";
        }
        
        ParseName(fullName);
    }
    
    public string FullName {
        get {
            string Format(string f, string l) => $"{f} {l}".Trim();
            return Format(_firstName, _lastName);
        }
    }
}

6. Async Local Function

public async Task ProcessBatchAsync(IEnumerable items) {
    async Task ProcessItemAsync(Item item) {
        await _repository.SaveAsync(item);
        _logger.LogInformation("Saved {Id}", item.Id);
    }
    
    foreach (var item in items) {
        await ProcessItemAsync(item);
    }
}

Limitations

  • Không thể có attributes (trừ khi dùng trên delegate conversion)
  • Không thể là static (trừ C# 8.0+ static local functions)
  • Không thể override/abstract/virtual
  • Không thể dùng làm event handlers trực tiếp

Static Local Functions (C# 8.0+)

public void Process(int[] data) {
    int multiplier = 2; // Captured variable
    
    // Static local function - KHÔNG capture outer variables
    static int Square(int x) => x * x;
    
    // Non-static - CÓ capture multiplier
    int Multiply(int x) => x * multiplier;
    
    var result = data.Select(x => Multiply(Square(x))).Sum();
}