programming
C#
Func delegate
out parameter
delegates

FuncT with out parameter

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

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

csharp
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:

csharp
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:

csharp
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:

csharp
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:

csharp
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:

csharp
// 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

csharp
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

csharp
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

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.