Back to Blog
C#

C# Generic Out Parameter: Type Inference Explained

Generic methods with out parameters have type-inference constraints at the call site. This article explains why explicit type arguments are often required and compares TryGetValue and tuple alternatives.

C# genericsout parameterstype inferenceTryGetValue patternC# methods
Diagram showing a C# generic method with an out parameter and the type inference constraint.

Generic methods with out parameters have the same runtime semantics as ordinary out parameters, but their type-inference behavior at the call site is more limited. The examples below show the syntax, why explicit type arguments are often required, and the patterns to use instead.

The Core Constraint: Type Inference and out Parameters

When you write a generic method that has an out parameter, the C# compiler's type inference behaves differently than with regular parameters. Consider a method that tries to parse a string into a generic type:

public static bool TryParse<T>(string input, out T result) { try { result = (T)Convert.ChangeType(input, typeof(T)); return true; } catch { result = default; return false; } }

Calling this method requires the type argument to be specified explicitly because the compiler does not infer T from an out argument:

if (TryParse<int>("42", out int value)) { Console.WriteLine(value); }

The out int value declaration does not participate in type inference. The compiler needs TryParse<int> to know what T is before it can check assignment compatibility of the out argument. C# also does not infer type arguments from a method's return type, so there is no call-context shortcut that can identify T here.

Why out Arguments Do Not Contribute to Type Inference

A common mistake is to assume that out var lets the compiler figure out T:

// Does not compile: type argument cannot be inferred if (TryParse("42", out var value)) { }

The var keyword only tells the compiler to infer the type of the local variable from the type of the out parameter. But the type of the out parameter depends on T, which has not been resolved. This creates a circular dependency: the compiler would need to know T to type the variable, but it would need the variable's type to infer T. C# resolves this by requiring an explicit type argument in this situation.

The same limitation applies when the argument has an explicit type such as out int value; the declaration has a type, but out parameters are not used as a source of type inference. It also applies to methods that use out parameters in generic interfaces and generic classes: the type parameter must come from another argument or from the constructed type.

The Generic TryGetValue Pattern

The most common real-world use of a generic out parameter is the TryGetValue pattern used by Dictionary<TKey, TValue> and similar collection types:

public static bool TryGetValue<TKey, TValue>( IDictionary<TKey, TValue> dictionary, TKey key, out TValue value) { if (dictionary.TryGetValue(key, out value)) { return true; } value = default; return false; }

Here the type arguments TKey and TValue are inferred from the dictionary and key parameters, not from the out parameter. The out TValue value parameter simply receives the inferred type. This is why the pattern works naturally with out var:

var config = new Dictionary<string, int> { ["retries"] = 3 }; if (TryGetValue(config, "retries", out var retries)) { Console.WriteLine(retries); }

The compiler infers TKey as string and TValue as int from the dictionary argument, so the out var local becomes int. The out parameter is a consumer of the inferred type, not a source of inference.

When You Must Specify Type Arguments Explicitly

If a generic method has an out parameter and no other parameter that can drive inference, you must write the type argument at the call site:

public static bool TryConvert<T>(string input, out T result) { // ... }

Every call must include the type argument:

if (TryConvert<double>("3.14", out double ratio)) { Console.WriteLine(ratio); }

This follows from the language's type-inference rules. An out argument declared as out int x has a known type, but out parameters do not contribute to inference; an out var argument has no type until inference completes, so it cannot be a source of inference either.

Generic Constraints and out Parameters

Generic constraints apply normally to methods that use out parameters. A method that requires a parameterless constructor can enforce that with where T : new():

public static bool TryCreate<T>(out T instance) where T : new() { instance = new T(); return true; }

Callers must specify T explicitly because there is no other parameter:

if (TryCreate<DateTime>(out var now)) { Console.WriteLine(now); }

The default keyword is often used to initialize an out parameter when the operation fails. For a reference type, default is null; for a value type, it is the zero-initialized value. This matters when the caller reads the out value after a false return, because the parameter is guaranteed to be assigned by the method before it returns.

Runtime Behavior and Assignment Guarantees

The compiler enforces definite assignment for out parameters. Every code path through the method must assign the parameter before returning. This is a compile-time guarantee, not a runtime check. In a generic method, this interacts with default(T):

public static bool TryFind<T>(IEnumerable<T> items, Func<T, bool> predicate, out T found) { foreach (var item in items) { if (predicate(item)) { found = item; return true; } } found = default; return false; }

The default assignment ensures the parameter is definitely assigned even when no item matches. The caller cannot rely on the value being meaningful when the method returns false, but the compiler guarantees it is not unassigned.

There is no boxing or reflection cost introduced by the out parameter itself. The parameter is a reference to the caller's storage location, and assignment writes directly to that location. For value types, the value is copied into the caller's variable; for reference types, the reference is copied. The generic machinery may introduce a small indirection for value types, but the out mechanism itself is the same as for non-generic methods.

Comparing out, ref, and Return-Value Approaches

When a generic method needs to return both a success flag and a value, there are three common designs:

ApproachCaller syntaxType inferenceTypical use
out parameterout var x or out int xDepends on other parametersTryGetValue patterns
ref parameterref int x (must be initialized)Can infer from the ref argument's typeIn-place mutation
Return a tuplevar (ok, x) = Method(...)Inferred from other arguments, not return type(bool, T) results

The tuple approach is often cleaner for new code when the type can be inferred from an ordinary argument:

public static (bool Success, T Value) TryFind<T>( IEnumerable<T> items, Func<T, bool> predicate) { foreach (var item in items) { if (predicate(item)) { return (true, item); } } return (false, default); }

The caller writes:

var (success, value) = TryFind(numbers, n => n > 10);

Here T is inferred from the items argument, and the tuple elements are typed accordingly. This avoids the explicit type argument requirement that a standalone out-only generic method imposes. Choose out when you want to match the established TryGetValue convention in a library API; choose a tuple when you control the API shape and want clearer call-site ergonomics.

C# Generic Out Parameter: Practical Usage and Code Examples | RYUSLOG DEV