2009-09-10 16 views
11

¿Cómo podría implementar mi propio mecanismo de ejecución diferida en C#?Ejecución diferida en C#

Así, por ejemplo, que tienen:

string x = DoFoo(); 

¿Es posible llevar a cabo un poco de magia para que DoFoo no se ejecuta hasta que "uso" x?

Respuesta

17

Puede utilizar lambdas/delegados:

Func<string> doit =() => DoFoo(); 
// - or - 
Func<string> doit = DoFoo; 

Más tarde se puede invocar doit al igual que un método:

string x = doit(); 

creo que lo más cerca que se puede conseguir es algo como esto:

Lazy<string> x = DoFoo; 

string y = x; // "use" x 

Con una definición de Lazy<T> similar a esto (no probado):

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; 
    } 
} 

Por desgracia, parece que los algoritmos de tipo de inferencia de que el compilador no puede auto-inferir el tipo de la Func<T> y por lo tanto no pueden igualar a la operador de conversión implícito. Debemos declarar explícitamente el tipo del delegado, lo que hace que las instrucciones de asignación más prolija:

// 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); 
+0

Pero, ¿cómo es "' string x = doit() '" cualquier cosa diferente de "' string x = DoFoo() '", aparte de la capa adicional de direccionamiento indirecto? – LukeH

+0

No lo es. No pude leer la pregunta. Mejorará la respuesta en un segundo ... – dtb

+0

Esto todavía llama a 'DoFoo()' antes de que x sea 'usado'. –

0

¿Por qué no simplemente no llamar a 'DoFoo()' hasta que lo desee?

- Edit

Quiero decir, ¿qué quiere decir "utilizar"

Por ejemplo, si desea que se le llame cuando '.ToString()' se llama, siempre se puede heredar la clase e implementar su función allí (pero esto sería muy poco intuitivo en mi humilde opinión).

1

En lugar de pasar una cadena x, pasar a un delegado que procura una cadena

Func<String> fooFunc=()=>DoFoo(); 
0

Más o menos mucho describir LINQ en acción. Una consulta de linq describe cómo obtener los datos, pero se recuperan los datos (se llama a DoFunc) solo cuando se itera la consulta. Considere si puede cambiar su diseño para aceptar IQueryable<string> donde necesita un string.

3

Si bien es un poco sucia siempre se puede utilizar 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()); 
} 
6

Una opción es utilizar la clase Lazy<T>, antes de la biblioteca de extensiones paralelas ahora una parte de .Net Framework 4.0.

Se le permite retrasar los datos de proceso de una manera consciente hilo.

+0

Prefiero usar una clase de sistema para hacerlo. Sin embargo, la clase del sistema no implementa operador implícito, lo que me llevó a buscar por qué. Encontré [esto] (http://netvignettes.wordpress.com/2011/04/24/implicit-conversion-operators-are-bad/), que explica por qué. –

Cuestiones relacionadas