207

Can I pass a method with an out parameter as a Func?

public IList<Foo> FindForBar(string bar, out int count) { }

// somewhere else
public IList<T> Find(Func<string, int, List<T>> listFunction) { }

Func needs a type so out won't compile there, and calling listFunction requires an int and won't allow an out in.

Is there a way to do this?

4 Answers 4

262

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);
2
  • 8
    In C# 4 (2010) and later (was not released when you wrote your answer) it is possible to mark T as contravariant, and V as covariant. However, since a parameter (output) of type U is passed by reference, U cannot be marked co- or contravariant and must remain "invariant". So consider public delegate V MyDelegate<in T, U, out V>(T input, out U output); if you use C# 4 or later. Commented Apr 22, 2014 at 10:04
  • 1
28

Why not create a class to encapsulate the results?

public class Result
{
     public IList<Foo> List { get; set; }
     public Int32 Count { get; set; }
}
1
  • 3
    Because newing-up a class object for every result will incur the GC tax, which is significant for high-frequency operations (so consider using ValueTuple instead).
    – Dai
    Commented Mar 24, 2022 at 9:31
18

The Func family of delegates (or Action for that matter) are nothing but simple delegate types declared like

//.NET 4 and above
public delegate TResult Func<out TResult>()
public delegate TResult Func<in T, out TResult>(T obj)

//.NET 3.5
public delegate TResult Func<T1, T2, TResult>(T1 obj1, T2 obj2)
public delegate TResult Func<T1, T2, T3, TResult>(T1 obj1, T2 obj2, T3 obj3)

etc. Delegates as such can have out/ref parameters, so in your case its only a matter of custom implementation by yourself as other answers have pointed out. As to why Microsoft did not pack this by default, think of the sheer number of combinations it would require.

delegate TResult Func<T1, T2, TResult>(T1 obj1, T2 obj2)
delegate TResult Func<T1, T2, TResult>(out T1 obj1, T2 obj2)
delegate TResult Func<T1, T2, TResult>(T1 obj1, out T2 obj2)
delegate TResult Func<T1, T2, TResult>(out T1 obj1, out T2 obj2)

for just two parameters. We have not even touched ref. It would actually be cumbersome and confusing for developers.

3
  • 3
    Note that C# function overloading cannot distinguish between delegate TResult Func<T1, T2, TResult>(T1 obj, T2 obj) and delegate TResult Func<T1, T2, TResult>(out T1 obj, T2 obj). So besides the number of overloads symbol name are an other reason for why Microsoft could not add these overloads of Func. Commented Oct 10, 2016 at 14:04
  • Can someone refer me to an MSDN article on the delegates above? Commented Oct 23, 2018 at 16:53
  • @SuLlewellyn I couldnt find the original msdn article, but you could try: learn.microsoft.com/en-us/dotnet/api/…, learn.microsoft.com/en-us/dotnet/api/…
    – nawfal
    Commented Oct 24, 2018 at 5:45
0

You could wrap it in a lambda/delegate/function/method that exposed the right interface and called FindForBar, but I suspect that FindForBar has count as an out parameter as a reason, so you'd need to be sure throwing that information away was ok/safe/desirable/had the right results (you'd need to be sure of this even if you could just directly pass in FindForBar).

Not the answer you're looking for? Browse other questions tagged or ask your own question.