Func <T> sin parámetro

167

¿Puedo pasar un método con un parámetro de salida como Func?

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

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

Func necesita un tipo para que out no se compile allí, y llamar a listFunction requiere un int y no permitirá una entrada.

¿Hay alguna forma de hacer esto?

blu
fuente

Respuestas:

228

refy outno forman parte de la definición de parámetro de tipo, por lo que no puede usar el Funcdelegado incorporado para pasar refy outargumentos. Por supuesto, puede declarar su propio delegado si desea:

delegate V MyDelegate<T,U,V>(T input, out U output);
Mehrdad Afshari
fuente
77
En C # 4 (2010) y posteriores (no se publicó cuando escribió su respuesta) es posible marcar Tcomo contravariante y Vcomo covariante. Sin embargo, dado que un parámetro ( output) de tipo Use pasa por referencia , Uno se puede marcar como co o contravariante y debe permanecer "invariante". Así que considere public delegate V MyDelegate<in T, U, out V>(T input, out U output);si usa C # 4 o posterior.
Jeppe Stig Nielsen
24

¿Por qué no crear una clase para encapsular los resultados?

public class Result
{
     public IList<Foo> List { get; set; }
     public Int32 Count { get; set; }
}
ChaosPandion
fuente
13

La Funcfamilia de delegados (o Actionpara el caso) no son más que simples tipos de delegados declarados como

//.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)

Los delegados como tales pueden tener parámetros out / ref, por lo que en su caso es solo una cuestión de implementación personalizada por usted mismo, como lo han señalado otras respuestas. En cuanto a por qué Microsoft no empacó esto por defecto, piense en la gran cantidad de combinaciones que requeriría.

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)

por solo dos parámetros. Ni siquiera nos hemos tocado ref. En realidad, sería engorroso y confuso para los desarrolladores.

nawfal
fuente
2
Tenga en cuenta que la sobrecarga de la función C # no puede distinguir entre delegate TResult Func<T1, T2, TResult>(T1 obj, T2 obj)y delegate TResult Func<T1, T2, TResult>(out T1 obj, T2 obj). Por lo tanto, además del número de símbolo de sobrecargas, hay otra razón por la cual Microsoft no pudo agregar estas sobrecargas Func.
Kasper van den Berg
¿Alguien puede referirme a un artículo de MSDN sobre los delegados anteriores?
Su Llewellyn
@SuLlewellyn No pude encontrar el artículo original de msdn, pero puedes probar: docs.microsoft.com/en-us/dotnet/api/… , docs.microsoft.com/en-us/dotnet/api/…
nawfal
0

Podría envolverlo en un método lambda / delegado / función / que expuso la interfaz correcta y llamó a FindForBar, pero sospecho que FindForBar cuenta como un parámetro de salida como una razón, por lo que debe asegurarse de tirar esa información ok / seguro / deseable / tuvo los resultados correctos (tendría que estar seguro de esto incluso si pudiera pasar directamente en FindForBar).

Logan Capaldo
fuente