porque - metodos anonimos c#
Acción/Func vs Métodos, ¿cuál es el punto? (5)
Sé cómo usar Action
and Func
en .NET, pero cada vez que comienzo a hacerlo, se puede lograr la misma solución con un método antiguo común al que llamo.
Esto excluye cuando una Action
o función se usa como un argumento para algo que no controlo, como LINQ''s .Where
.
Así que básicamente mi pregunta es ... ¿por qué existen? ¿Qué me dan extra y nuevo que un método simple no?
Action y Func son tipos de Delegate proporcionados por el marco. Los delegados permiten que las funciones se traten como variables, lo que significa que puede (entre otras cosas) pasarlas de un método a otro. Si alguna vez ha programado en C ++, puede pensar en los delegados como punteros a funciones que están restringidos por la firma del método al que se refieren.
Action y Func son específicamente delegados genéricos (lo que significa que toman parámetros de tipo) con algunas de las firmas más comunes; casi cualquier método en la mayoría de los programas puede representarse usando uno u otro de esos dos, ahorrando a las personas mucho tiempo definiendo delegados manualmente como Lo hicimos en .net antes de la versión 2. De hecho, cuando veo un código como este en un proyecto, generalmente puedo asumir con seguridad que el proyecto se migró desde .net 1.1:
// This defines a delegate (a type that represents a function)
// but usages could easily be replaced with System.Action<String>
delegate void SomeApplicationSpecificName(String someArgument);
Te recomiendo que mires a los delegados un poco más. Son una característica muy poderosa del lenguaje C #.
Creo que otras respuestas aquí hablan de lo que es un Action
/ Func
y su uso. Intentaré responder cómo elegir entre Action
/ Func
y método. Las diferencias primero:
1) Desde un punto de vista de rendimiento sin procesar, los delegados son más lentos en comparación con las llamadas de método directo , pero es tan insignificante que preocuparse por ello es una mala práctica.
2) Los métodos pueden tener sobrecargas (los mismos nombres de funciones con firmas diferentes) pero no los delegados de Action
/ Func
ya que se declaran como variables y, según las reglas de C #, no puede tener dos variables con el mismo nombre en un ámbito determinado.
bool IsIt() { return 1 > 2; }
bool IsIt(int i) { return i > 2; } //legal
Func<bool> IsIt = () => 1 > 2;
Func<int, bool> IsIt = i => i > 2; //illegal, duplicate variable naming
3) En consecuencia, Action
/ Func
son reasignables y pueden apuntar a cualquier función, mientras que los métodos una vez compilados siguen siendo los mismos para siempre. Es semánticamente incorrecto usar Func/Action
si el método al que apunta nunca cambia durante el tiempo de ejecución.
bool IsIt() { return 1 > 2; } //always returns false
Func<bool> IsIt = () => 1 > 2;
IsIt = () => 2 > 1; //output of IsIt depends on the function it points to.
4) Puede especificar out
parámetros de ref
/ out
para los métodos normales. Por ejemplo, puedes tener
bool IsIt(out string p1, ref int p2) { return 1 > 2; } //legal
Func<out string, ref int, bool> IsIt; //illegal
5) No puede introducir un nuevo parámetro de tipo genérico para Action
/ Func
(ya son genéricos por cierto, pero los argumentos de tipo solo pueden ser un tipo conocido o tipos especificados en el método o clase principal), a diferencia de los métodos.
bool IsIt<A, R>() { return 1 > 2; } //legal
Func<bool> IsIt<A, R> = () => 1 > 2; //illegal
6) Los métodos pueden tener parámetros opcionales, no Action
/ Func
.
bool IsIt(string p1 = "xyz") { return 1 > 2; } //legal
Func<string, bool> IsIt = (p1 = "xyz") => 1 > 2; //illegal
7) Puede tener la palabra clave params
para los parámetros de un método, no así con Action
/ Func
.
bool IsIt(params string[] p1) { return 1 > 2; } //legal
Func<params string[], bool> IsIt = p1 => 1 > 2; //illegal
8) Intellisense se desempeña bien con los nombres de los parámetros de los métodos (y, por lo tanto, tiene disponible una excelente documentación XML para los métodos), no así con Action
/ Func
. Por lo que respecta a la legibilidad, los métodos regulares ganan.
9) Action
/ Func
tiene un límite de parámetro de 16 (no es que no puedas definir tus propios con más), pero los métodos admiten más de lo que nunca necesitarás.
En cuanto a cuándo usar cuál, consideraría lo siguiente:
Cuando se ve obligado a usar uno basado en cualquiera de los puntos anteriores, de todos modos no tiene otra opción. El punto 3 es el más convincente que encuentro sobre el cual tendrá que basar su decisión.
En la mayoría de los casos normales, un método regular es el camino a seguir. Es la forma estándar de refactorizar un conjunto de funcionalidades comunes en el mundo C # y VB.NET.
Como regla general, si la función es más que una línea, prefiero un método.
Si la función no tiene relevancia fuera de un método específico y la función es demasiado trivial, como un selector simple (
Func<S, T>
) o un predicado (Func<bool>
), preferiríaAction
/Func
. Por ejemplo,public static string GetTimeStamp() { Func<DateTime, string> f = dt => humanReadable ? dt.ToShortTimeString() : dt.ToLongTimeString(); return f(DateTime.Now); }
Podría haber situaciones en las que
Action
/Func
tenga más sentido. Por ejemplo, si tiene que construir una expresión pesada y compilar un delegado, vale la pena hacerlo solo una vez y almacenar en caché al delegado compilado.public static class Cache<T> { public static readonly Func<T> Get = GetImpl(); static Func<T> GetImpl() { //some expensive operation here, and return a compiled delegate } }
en lugar de
public static class Cache<T> { public static T Get() { //build expression, compile delegate and invoke the delegate } }
En el primer caso cuando llama a
Get
,GetImpl
se ejecuta solo una vez, mientras que en el segundo caso,Get
(costoso) se llamará cada vez.
No olvidar el método anónimo en sí tendrá ciertos límites no relacionados con Func/Action
, lo que hace que el uso sea un poco diferente. También vea esto para una pregunta relacionada.
Hay muchos casos en los que un Func puede ayudar donde un Método no.
public void DoThing(MyClass foo, Func<MyClass, string> func)
{
foo.DoSomething;
var result = func(foo);
foo.DoStringThing(result);
}
Por lo tanto, puede especificar un Func diferente cada vez que llame a este método: el método DoThing
no necesita saber qué se está haciendo, solo que, sea lo que sea, devolverá una cadena.
Puede hacer esto sin usar la palabra clave Func usando la palabra clave delegate
lugar; Funciona de la misma manera.
Los uso para crear una serie de funciones. Por ejemplo, puedo tener un ComboBox lleno de acciones que se podrían tomar. Relleno el ComboBox con elementos de una clase o estructura:
public class ComboBoxAction
{
private string text;
private Action method;
public ComboBoxAction(string text, Action method)
{
this.text = text;
this.method = method;
}
public override string ToString()
{
return this.text;
}
public void Go()
{
this.method();
}
}
Luego, cuando alguien selecciona un elemento, puedo invocar la acción.
CType(ComboBox1.SelectedItem, ComboBoxAction).Go()
Esto es mucho más fácil que tener una declaración Select que determine qué método llamar según el texto del ComboBox.
Un gran uso de la action
y la func
es cuando necesitamos realizar alguna operación (antes o después de un método), independientemente de cuál sea el método. Por ejemplo, debemos reintentar el método 10 veces si se produce una excepción.
Considere el siguiente método: su tipo de retorno es generic
. Por lo tanto, se puede aplicar en func
con cualquier tipo de retorno.
public static T ExecuteMultipleAttempts<T>(Func<T> inputMethod, Action additionalTask, int wait, int numOfTimes)
{
var funcResult = default(T);
int counter = 0;
while (counter < numOfTimes)
{
try
{
counter++;
funcResult = inputMethod();
//If no exception so far, the next line will break the loop.
break;
}
catch (Exception ex)
{
if (counter >= numOfTimes)
{
//If already exceeded the number of attemps, throw exception
throw;
}
else
{
Thread.Sleep(wait);
}
if (additionalTask != null)
{
additionalTask();
}
}
}
return funcResult;
}