Func<T> with Out Parameter

Func T with out parameter

ref and out are not part of the type parameter definition so you can't use the built-in Func delegate to pass ref and out arguments. Of course, you can declare your own delegate if you want:

delegate V MyDelegate<T,U,V>(T input, out U output);

out parameter modifier in Func delegate (C#)

The Short Answer

You rarely need to worry about the in and out keywords in Generic type definitions. A class defined with in/out generic type parameters will usually "just work" when you're consuming it, and I'd wager that most devs will never write such a definition in their own code.

The Longer Answer

To get a complete explanation, you should read Covariance and Contravariance and Variance in Delegates. The rest of my answer is just some illustrative example code.

To simplify the explanation, I'm going explain in and out separately through Action<T> and Func<TResult> instead of Func<T,TResult>.

The examples all use the following two classes:

class BaseClass {}
class DerivedClass : BaseClass {}

Covariance: out

For this example, I've mimicked Func<out TResult>, but removed the out (covariance) modifier to demonstrate its effect. Covariance allows us to use a func that returns DerivedType anywhere that expects a func that returns BaseType.

class CovarianceExamples
{
// This is similar to System.Func<out TResult>(), but with covariance removed
delegate TResult InvariantFunc<TResult>();

void InvariantFuncExample()
{
// Ignore the values of these variables; it's the types that are important
InvariantFunc<BaseClass> baseFunc = null;
InvariantFunc<DerivedClass> derivedFunc = null;

baseFunc = baseFunc; // Allowed
baseFunc = derivedFunc; // Not allowed; compile error!
}

void CovariantFuncExample()
{
// Ignore the values of these variables; it's the types that are important
Func<BaseClass> baseFunc = null;
Func<DerivedClass> derivedFunc = null;

baseFunc = baseFunc; // Allowed
baseFunc = derivedFunc; // Allowed
}
}

Contravariance: in

For this example, I've mimicked Action<in T>, but removed the in (contravariance) modifier to demonstrate its effect. Contravariance allows us to use an action that accepts BaseType anywhere that expects an action that accepts DerivedType.

class ContravarianceExamples
{
// This is similar to System.Action<in T>(T), but with contravariance removed
delegate void InvariantAction<T>();

void InvariantActionExample()
{
// Ignore the values of these variables; it's the types that are important
InvariantAction<BaseClass> baseAction = null;
InvariantAction<DerivedClass> derivedAction = null;

baseAction = baseAction; // Allowed
derivedAction = baseAction; // Not allowed; compile error!
}

void ContravariantActionExample()
{
// Ignore the values of these variables; it's the types that are important
Action<BaseClass> baseAction = null;
Action<DerivedClass> derivedAction = null;

baseAction = baseAction; // Allowed
derivedAction = baseAction; // Allowed
}
}

Using a out parameter in Func Delegate

You are just written answer.

If you in .net 4.0 or above you can specify variance for parameters.

public delegate TV MyFunc<in T, TU, out TV>(T input, out TU output);

Then use:

bool TryGetItem(string itemKey,out Item item);

MyFunc<string, Item, bool> func = TryGetItem;

Can I have an Action or Func with an out param?

Action and Func specifically do not take out or ref parameters. However, they are just delegates.

You can make a custom delegate type that does take an out parameter, and use it, though.

For example, the following works:

class Program
{
static void OutFunc(out int a, out int b) { a = b = 0; }

public delegate void OutAction<T1,T2>(out T1 a, out T2 b);

static void Main(string[] args)
{
OutAction<int, int> action = OutFunc;
int a = 3, b = 5;
Console.WriteLine("{0}/{1}",a,b);
action(out a, out b);
Console.WriteLine("{0}/{1}", a, b);
Console.ReadKey();
}
}

This prints out:

3/5
0/0

Implement calling delegate with out parameters

Just use the same scheme as in your example of SafetyExecuteFunction<T>(Func<T> func).

One thing you have to pay attention to is that you need to use a temporary local variable for the out parameter.

private TResult SafetyExecuteFunctionWithOut<T1, T2, TResult>(FuncWithOut<T1, T2, TResult> func, T1 arg1, out T2 arg2)
{
TResult result = default(TResult);
T2 arg2Result = default(T2); // Need to use a temporary local variable here

SafetyExecuteMethod(() => result = func(arg1, out arg2Result));

arg2 = arg2Result; // And then assign it to the actual parameter after calling the delegate.
return result;
}

Calling the function does then work like this:

public bool CanUpdateUser(string userName, out string errorMessage)
{
bool result = SafetyExecuteFunctionWithOut<string, string, bool>(_innerSession.CanUpdateUser, userName, out errorMessage);
return result;
}

Note, that you have to pass _innerSession.CanUpdateUser as a parameter to SafetyExecuteFunctionWithOut instead of using a lambda expression.


Using the naive attempt:

private TResult SafetyExecuteFunctionWithOut<T1, T2, TResult>(FuncWithOut<T1, T2, TResult> func, T1 arg1, out T2 arg2)
{
TResult result = default(TResult);

SafetyExecuteMethod(() => result = func(arg1, out arg2));

return result;
}

creates the error message:

CS1628 Cannot use ref or out parameter 'arg2' inside an anonymous
method, lambda expression, or query expression

Why you are not allowed to do that is explained in this answer.

Using Func T delegate in C#

In both cases, the lambda expressions that you have written:

()=>GreatherThan(1,2)
()=> calculateTwo(2)

represent functions with no parameters, as indicated by the empty brackets (). It does not matter what you do inside the lambda expression. () => calculateTwo(2) represents a function with no parameters, that calls calculateTwo(2) as the only thing it does. Imagine these:

int AnonymousFunction1() {
return calculateTwo(2);
}
bool AnonymousFunction2() {
return GreaterThan(1, 2);
}

Think of as these as what you are actually passing to Task.Run by writing those lambda expressions. These functions are clearly compatible with Func<int> and Func<bool>, and not compatible with Func<int, int, bool>.

How to declare a generic delegate with an out parameter

Actually, Func is just a simple delegate declared in the .NET Framework. Actually, there are several Func delegates declared there:

delegate TResult Func<TResult>()
delegate TResult Func<T, TResult>(T obj)
delegate TResult Func<T1, T2, TResult>(T1 obj1, T2 obj2)
delegate TResult Func<T1, T2, T3, TResult>(T1 obj1, T2 obj2, T3 obj3)
delegate TResult Func<T1, T2, T3, T4, TResult>(T1 obj1, T2 obj2, T3 obj3, T4 obj4)
delegate TResult Func<T1, T2, ... , T16, TResult>(T1 obj1, T2 obj2, ..., T16 obj16)

So the only thing you can do is declare your custom delegate:

delegate bool MyFunc<T1, T2>(T1 a, out T2 b)


Related Topics



Leave a reply



Submit