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

Ref Locals và Ref Returns trong C# 7.0

Reference return value cho phép method trả về reference đến variable, thay vì value.

Caller có thể chọn treat returned variable như thể nó được trả về by value hoặc by reference.

Caller có thể tạo variable mới là reference đến returned value, gọi là ref local.

Trong ví dụ dưới, dù modify color nó không có impact trên original array colors.

Ví dụ: Không dùng ref (pass by value)

class Program { 
    public static void Main() { 
        var colors = new[] { "blue", "green", "yellow", "orange", "pink" }; 
        string color = colors[3]; 
        color = "Magenta"; 
        System.Console.WriteLine(String.Join(" ", colors)); 
        Console.ReadLine(); 
    } 
}

Output

blue green yellow orange pink

Dùng Ref Locals

public static void Main() { 
    var colors = new[] { "blue", "green", "yellow", "orange", "pink" }; 
    ref string color = ref colors[3]; 
    color = "Magenta"; 
    System.Console.WriteLine(String.Join(" ", colors)); 
    Console.ReadLine(); 
}

Output

blue green yellow Magenta pink

Ref Returns

Trong ví dụ dưới, dù modify color không impact original array colors.

class Program { 
    public static void Main() { 
        var colors = new[] { "blue", "green", "yellow", "orange", "pink" }; 
        string color = GetColor(colors, 3); 
        color = "Magenta"; 
        System.Console.WriteLine(String.Join(" ", colors)); 
        Console.ReadLine(); 
    } 
    
    public static string GetColor(string[] col, int index) { 
        return col[index]; 
    } 
}

Output

blue green yellow orange pink

Dùng Ref Return

class Program { 
    public static void Main() { 
        var colors = new[] { "blue", "green", "yellow", "orange", "pink" }; 
        ref string color = ref GetColor(colors, 3); 
        color = "Magenta"; 
        System.Console.WriteLine(String.Join(" ", colors)); 
        Console.ReadLine(); 
    } 
    
    public static ref string GetColor(string[] col, int index) { 
        return ref col[index]; 
    } 
}

Output

blue green yellow Magenta pink

Ref Locals vs Ref Returns - Tóm tắt

Concept Syntax Mục đích
Ref Local ref T variable = ref expression; Tạo alias đến existing variable/element
Ref Return ref T Method() { return ref variable; } Trả về reference thay vì copy value
Ref Readonly Local ref readonly T variable = ref expression; Alias readonly (C# 7.2+)
Ref Readonly Return ref readonly T Method() { return ref variable; } Trả về readonly reference (C# 7.2+)

Quy tắc quan trọng

  • Ref return phải return ref đến variable có lifetime ≥ method (fields, array elements, ref parameters). Không return ref đến local variable của method.
  • Ref local phải initialized ngay khi khai báo.
  • Ref local không thể gán null (trừ nullable ref types C# 8.0+).
  • Có thể gán ref local cho ref local khác: ref var alias = ref original;

Use Cases Thực Tế

1. High-performance array/span manipulation

public ref T FindMax(Span span) where T : IComparable {
    ref T max = ref span[0];
    for (int i = 1; i < span.Length; i++) {
        if (span[i].CompareTo(max) > 0) {
            max = ref span[i];
        }
    }
    return ref max;
}

// Usage
var numbers = new[] { 1, 5, 3, 9, 2 };
ref int maxRef = ref FindMax(numbers);
maxRef = 100; // Modifies original array!
// numbers = [1, 5, 3, 100, 2]

2. Avoiding defensive copies (structs lớn)

public struct LargeStruct {
    public long A, B, C, D, E, F, G, H;
    
    // BAD: Returns copy (80 bytes)
    public LargeStruct GetBad() => this;
    
    // GOOD: Returns reference (no copy)
    public ref readonly LargeStruct GetGood() => ref this;
}

3. Dictionary/Collection helpers

public static ref TValue GetOrAddRef(
    this Dictionary dict, TKey key, Func factory) 
    where TValue : struct {
    
    if (dict.TryGetValue(key, out var existing)) {
        // Cần unsafe hoặc CollectionsMarshal (NET 5+) để get ref
        // Trước NET 5: không thể return ref đến dictionary value trực tiếp
    }
    // ...
}

// .NET 5+ CollectionsMarshal
ref var valueRef = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _);
valueRef = newValue; // Modify in-place

C# 11+: ref fields và ref struct

// Ref struct - chỉ sống trên stack
public ref struct SpanWrapper {
    private Span _span;
    
    public SpanWrapper(Span span) => _span = span;
    
    public ref int this[int index] => ref _span[index];
}

// Ref fields (C# 11+) - trong ref struct
public ref struct Node {
    public int Value;
    public ref Node Next; // Ref field
}