Ejecución diferida en C #
-
05-07-2019 - |
Pregunta
¿Cómo podría implementar mi propio mecanismo de ejecución diferida en C #?
Entonces, por ejemplo, tengo:
string x = DoFoo();
¿Es posible realizar algo de magia para que DoFoo no se ejecute hasta que yo " use " x?
Solución
Puede usar lambdas / delegados:
Func<string> doit = () => DoFoo();
// - or -
Func<string> doit = DoFoo;
Más tarde puede invocar doit
como un método:
string x = doit();
Creo que lo más cerca que puedes estar es algo como esto:
Lazy<string> x = DoFoo;
string y = x; // "use" x
Con una definición de Lazy<T>
similar a esta (sin probar):
public class Lazy<T>
{
private readonly Func<T> func;
private bool hasValue;
private T value;
public Lazy(Func<T> func)
{
this.func = func;
this.hasValue = false;
}
public static implicit operator Lazy<T>(Func<T> func)
{
return new Lazy<T>(func);
}
public static implicit operator T(Lazy<T> lazy)
{
if (!lazy.hasValue)
{
lazy.value = lazy.func();
lazy.hasValue = true;
}
return lazy.value;
}
}
Desafortunadamente, parece que los algoritmos de inferencia de tipos del compilador no pueden inferir automáticamente el tipo de Func<T>
y, por lo tanto, no pueden coincidir con el operador de conversión implícito. Necesitamos declarar explícitamente el tipo de delegado, lo que hace que las declaraciones de asignación sean más detalladas:
// none of these will compile...
Lazy<string> x = DoFoo;
Lazy<string> y = () => DoFoo();
Lazy<string> z = delegate() { return DoFoo(); };
// these all work...
Lazy<string> a = (Func<string>)DoFoo;
Lazy<string> b = (Func<string>)(() => DoFoo());
Lazy<string> c = new Func<string>(DoFoo);
Lazy<string> d = new Func<string>(() => DoFoo());
Lazy<string> e = new Lazy<string>(DoFoo);
Lazy<string> f = new Lazy<string>(() => DoFoo);
Otros consejos
Una opción es usar la clase Lazy<T>
, anteriormente de la biblioteca de extensiones paralelas que ahora forma parte de .Net Framework 4.0.
Le permite retrasar los datos del proceso de manera consciente.
Si bien está algo sucio, siempre puedes usar la palabra clave de rendimiento:
public IEnumerable<int> DoFoo() {
Console.WriteLine("doing foo");
yield return 10;
}
[Test]
public void TestMethod()
{
var x = DoFoo();
Console.WriteLine("foo aquired?");
Console.WriteLine(x.First());
}
En lugar de pasar una cadena x , pase un delegado que le proporcione una cadena
Func<String> fooFunc=()=>DoFoo();
¿Por qué no simplemente no llamar a 'DoFoo ()' hasta que lo desee?
- Editar
Quiero decir, ¿qué quieres decir con " usa "
Por ejemplo, si desea que se llame cuando se llame '.ToString ()', siempre puede heredar la clase e implementar su función allí (pero esto sería bastante poco intuitivo).
Describes más o menos LINQ en acción. Una consulta linq describe cómo obtener los datos, pero los datos se recuperan (se llama a DoFunc) solo cuando la consulta se repite. Considere si puede cambiar su diseño para aceptar IQueryable<string>
donde necesita un string
.