Có thể khai báo out values inline như arguments của method nơi chúng được dùng.
Existing out parameters đã được cải tiến trong version này. Giờ có thể khai báo out variables trong argument list của method call, thay vì viết separate declaration statement.
Ưu điểm
- Code dễ đọc hơn.
- Không cần assign initial value.
Existing Syntax (Trước C# 7.0)
class Program {
public static void AddMultiplyValues(int a, int b, out int c, out int d) {
c = a + b;
d = a * b;
}
public static void Main() {
int c;
int d;
AddMultiplyValues(5, 10, out c, out d);
System.Console.WriteLine(c);
System.Console.WriteLine(d);
Console.ReadLine();
}
}
Output
15 50
New Syntax (C# 7.0+)
class Program {
public static void AddMultiplyValues(int a, int b, out int c, out int d) {
c = a + b;
d = a * b;
}
public static void Main() {
AddMultiplyValues(5, 10, out int c, out int d);
System.Console.WriteLine(c);
System.Console.WriteLine(d);
Console.ReadLine();
}
}
Output
15 50
Out Variables Chi Tiết
Scope của Out Variables
public void Demo() {
// Out variable scope: trong block chứa method call
GetValues(out int x, out int y);
Console.WriteLine($"{x}, {y}"); // x, y accessible here
if (TryParse(input, out int number)) {
Console.WriteLine(number); // number accessible trong if block
}
// number KHÔNG accessible ở đây (nếu C# 7.0-7.2)
// C# 7.3+: number accessible ở đây (scope mở rộng)
}
Var Type Inference
// Compiler infer type từ method signature GetCoordinates(out var x, out var y); // x, y inferred as int // Với generic methods void GetValue(out T value) { ... } GetValue(out var value); // T inferred from context or default
Discards với Out Parameters (C# 7.0+)
// Bỏ qua out parameter không cần
GetCoordinates(out int x, out _); // Chỉ cần x
GetCoordinates(out _, out _); // Bỏ qua cả hai
// TryParse pattern phổ biến
if (int.TryParse(input, out _)) {
// Chỉ check parse success, không cần value
}
Out vs Ref vs In
| Keyword | Direction | Initialization Required | Caller Must Initialize | C# 7.0 Inline Declaration |
|---|---|---|---|---|
| out | Out-only (write) | Method PHẢI assign trước return | Không (C# 7.0+) | ✅ out int x |
| ref | In-Out (read/write) | Method có thể read/write | Có (phải initialized) | ✅ ref int x (C# 7.0+) |
| in | In-only (read) | Method KHÔNG được modify | Có | ❌ Không hỗ trợ inline |
Ref Inline Declaration (C# 7.0+)
void Swap(ref int a, ref int b) {
int temp = a; a = b; b = temp;
}
var x = 1; var y = 2;
Swap(ref x, ref y); // Traditional
Swap(ref int a = ref x, ref int b = ref y); // C# 7.0+ inline (rarely used)
TryParse Pattern - Best Practice
// Trước C# 7.0
int number;
bool success = int.TryParse(input, out number);
if (success) { ... }
// C# 7.0+ - Clean hơn
if (int.TryParse(input, out int number)) {
Console.WriteLine($"Parsed: {number}");
}
// C# 7.0+ với discard
if (int.TryParse(input, out _)) {
Console.WriteLine("Valid integer format");
}
// Pattern matching (C# 7.0+)
if (input is int number) { // Chỉ work với constant pattern, không TryParse
// ...
}
// Custom TryParse pattern
public bool TryGetUser(int id, out User user) {
user = _repository.GetById(id);
return user != null;
}
// Usage
if (TryGetUser(123, out var user)) {
Console.WriteLine(user.Name);
}
Async Out Parameters (Not Supported)
C# không support async out parameters. Workarounds:
// 1. Return tuple
public async Task<(bool Success, User User)> TryGetUserAsync(int id) {
var user = await _repository.GetByIdAsync(id);
return (user != null, user);
}
// 2. Custom result class
public class TryResult {
public bool Success { get; }
public T Value { get; }
public Exception Error { get; }
public static TryResult Success(T value) => new TryResult { Success = true, Value = value };
public static TryResult Failure(Exception ex) => new TryResult { Success = false, Error = ex };
}
public async Task> TryGetUserAsync(int id) {
try {
var user = await _repository.GetByIdAsync(id);
return user != null ? TryResult.Success(user) : TryResult.Failure(new KeyNotFoundException());
} catch (Exception ex) {
return TryResult.Failure(ex);
}
}