Introduction
In C#, Func<T> delegates cannot directly use out or ref parameters because the generic type parameter constraints do not support them. The Func<T1, TResult> signature expects regular value or reference types as parameters. To work around this, you can define a custom delegate, use tuple returns, or restructure your code to avoid out parameters entirely.
The Problem
1// This does NOT compile
2Func<string, out int, bool> tryParse;
3// Error: 'out' is not valid in this context
4
5// You want to wrap something like:
6// bool int.TryParse(string s, out int result)
Func<> generic type parameters do not support the out or ref modifiers.
Solution 1: Custom Delegate (Direct Fix)
Define your own delegate type with the out parameter:
1// Custom delegate that supports 'out'
2delegate bool TryParseDelegate<T>(string input, out T result);
3
4// Usage
5TryParseDelegate<int> tryParse = int.TryParse;
6
7if (tryParse("42", out int value))
8{
9 Console.WriteLine(value); // 42
10}
11
12// Works with any TryParse-style method
13TryParseDelegate<double> tryParseDouble = double.TryParse;
14TryParseDelegate<DateTime> tryParseDate = DateTime.TryParse;
Solution 2: Return a Tuple Instead
Wrap the out parameter method to return a tuple:
1// Wrap TryParse to return (success, value) tuple
2Func<string, (bool success, int value)> tryParse = s =>
3{
4 bool ok = int.TryParse(s, out int result);
5 return (ok, result);
6};
7
8var (success, value) = tryParse("42");
9if (success)
10{
11 Console.WriteLine(value); // 42
12}
13
14// Generic version
15static Func<string, (bool, T)> MakeTryParse<T>(TryParseDelegate<T> parser)
16{
17 return s =>
18 {
19 bool ok = parser(s, out T result);
20 return (ok, result);
21 };
22}
23
24var parseInt = MakeTryParse<int>(int.TryParse);
25var parseDouble = MakeTryParse<double>(double.TryParse);
Solution 3: Use a Wrapper Class
Encapsulate the result in a class:
1public class ParseResult<T>
2{
3 public bool Success { get; }
4 public T Value { get; }
5
6 public ParseResult(bool success, T value)
7 {
8 Success = success;
9 Value = value;
10 }
11}
12
13Func<string, ParseResult<int>> tryParse = s =>
14{
15 bool ok = int.TryParse(s, out int result);
16 return new ParseResult<int>(ok, result);
17};
18
19var result = tryParse("42");
20if (result.Success)
21{
22 Console.WriteLine(result.Value); // 42
23}
Solution 4: Use Nullable Return (C# 8+)
For value types, return null on failure:
1Func<string, int?> tryParse = s =>
2 int.TryParse(s, out int result) ? result : (int?)null;
3
4int? value = tryParse("42");
5if (value.HasValue)
6{
7 Console.WriteLine(value.Value); // 42
8}
9
10// With pattern matching
11if (tryParse("42") is int parsed)
12{
13 Console.WriteLine(parsed);
14}
This is the cleanest approach for TryParse-style methods on value types.
Why Func Does Not Support out/ref
The Func<> and Action<> delegates use generic type parameters:
// Func definition (simplified)
public delegate TResult Func<in T, out TResult>(T arg);
The in/out here are variance modifiers (covariance/contravariance), not the out parameter modifier. C# generics cannot express out T or ref T as parameter types — this is a limitation of the CLR generic type system.
Practical Example: Dictionary TryGetValue
1// Can't directly: Func<string, out string, bool> getter = dict.TryGetValue;
2
3// Solution: wrap it
4var dict = new Dictionary<string, string>
5{
6 ["key1"] = "value1",
7 ["key2"] = "value2"
8};
9
10Func<string, (bool found, string value)> tryGet = key =>
11{
12 bool ok = dict.TryGetValue(key, out string val);
13 return (ok, val);
14};
15
16var (found, val) = tryGet("key1");
17if (found) Console.WriteLine(val); // value1
Passing to Higher-Order Functions
1// Filter a list of strings, keeping only valid integers
2List<string> inputs = new() { "1", "abc", "3", "def", "5" };
3
4// Using the tuple wrapper approach
5Func<string, (bool ok, int val)> parse = s =>
6 (int.TryParse(s, out int v), v);
7
8List<int> validInts = inputs
9 .Select(parse)
10 .Where(r => r.ok)
11 .Select(r => r.val)
12 .ToList();
13// [1, 3, 5]
14
15// Or more concisely with nullable
16List<int> validInts2 = inputs
17 .Select(s => int.TryParse(s, out int v) ? (int?)v : null)
18 .Where(v => v.HasValue)
19 .Select(v => v.Value)
20 .ToList();
Common Pitfalls
Confusing variance out with parameter out: In Func<in T, out TResult>, out means covariant return type — completely different from the out parameter modifier.
Lambda capture of out variables: You cannot capture an out parameter in a lambda. The lambda must call the method internally and capture the result.
Performance: Wrapping in tuples or classes adds a small allocation overhead. For hot paths, use the custom delegate approach which has zero overhead.
Async methods: async methods cannot have out parameters either. Return a tuple or result object instead: Task<(bool, T)>.
Default values: When using int? return type, ensure callers handle null properly. Use value ?? defaultValue or pattern matching.
Summary
Func<T> cannot have out or ref parameters due to CLR generic limitations
Define a custom delegate (delegate bool TryParse<T>(string s, out T result)) for direct support
Return a tuple (bool success, T value) to wrap out parameter methods into Func<>
Use int? nullable returns for TryParse-style methods as the cleanest workaround
The variance out in Func<out TResult> is unrelated to the parameter out modifier