199

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?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
blu
  • 12,905
  • 20
  • 70
  • 106

4 Answers4

256

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);
Spencer Ruport
  • 34,865
  • 12
  • 85
  • 147
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 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(T input, out U output);` if you use C# 4 or later. – Jeppe Stig Nielsen Apr 22 '14 at 10:04
  • 1
    See [this example for using `ref` parameter with `in` and `out` parameters](http://stackoverflow.com/a/24044772/175679). – SliverNinja - MSFT Jun 04 '14 at 18:21
28

Why not create a class to encapsulate the results?

public class Result
{
     public IList<Foo> List { get; set; }
     public Int32 Count { get; set; }
}
ChaosPandion
  • 77,506
  • 18
  • 119
  • 157
  • 2
    Because `new`ing-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 Mar 24 '22 at 09: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.

nawfal
  • 70,104
  • 56
  • 326
  • 368
  • 3
    Note that C# function overloading cannot distinguish between `delegate TResult Func(T1 obj, T2 obj)` and `delegate TResult Func(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`. – Kasper van den Berg Oct 10 '16 at 14:04
  • Can someone refer me to an MSDN article on the delegates above? – Su Llewellyn Oct 23 '18 at 16:53
  • @SuLlewellyn I couldnt find the original msdn article, but you could try: https://learn.microsoft.com/en-us/dotnet/api/system.func-2?view=netframework-4.7.2, https://learn.microsoft.com/en-us/dotnet/api/system.action?view=netframework-4.7.2 – nawfal Oct 24 '18 at 05: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).

Logan Capaldo
  • 39,555
  • 5
  • 63
  • 78