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
}