Esecuzione differita in C #
-
05-07-2019 - |
Domanda
Come potrei implementare il mio meccanismo di esecuzione differita in C #?
Quindi ad esempio ho:
string x = DoFoo();
È possibile eseguire un po 'di magia in modo che DoFoo non venga eseguito fino a quando " usa " x?
Soluzione
Puoi usare lambdas / delegates:
Func<string> doit = () => DoFoo();
// - or -
Func<string> doit = DoFoo;
Successivamente puoi invocare doit
proprio come un metodo:
string x = doit();
Penso che il più vicino che puoi ottenere sia qualcosa del genere:
Lazy<string> x = DoFoo;
string y = x; // "use" x
Con una definizione di Lazy<T>
simile a questa (non testato):
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;
}
}
Sfortunatamente, sembra che gli algoritmi di inferenza del tipo del compilatore non possano inferire automaticamente il tipo di Func<T>
e quindi non possano corrispondere all'operatore di conversione implicito. Dobbiamo dichiarare esplicitamente il tipo di delegato, il che rende le dichiarazioni di assegnazione più dettagliate:
// 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);
Altri suggerimenti
Un'opzione è usare la classe Lazy<T>
, precedentemente dalla libreria di estensioni parallele ora parte di .Net Framework 4.0.
Ti consente di ritardare i dati di processo in modo consapevole del thread.
Anche se è un po 'sporco, puoi sempre usare la parola chiave yield:
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());
}
Invece di passare una stringa x , passa un delegato che ti procura una stringa
Func<String> fooFunc=()=>DoFoo();
Perché non chiamare semplicemente 'DoFoo ()' fino a quando non lo desideri?
- Modifica
Voglio dire, cosa intendi con " usa "
Ad esempio, se vuoi che venga chiamato quando viene chiamato '.ToString ()', puoi sempre ereditare la classe e implementare lì la tua funzione (ma questo sarebbe IMHO abbastanza non intuitivo).
Descrivi praticamente LINQ in azione. Una query linq descrive come ottenere i dati, ma i dati vengono recuperati (viene chiamato DoFunc) solo quando la query viene ripetuta. Considera se puoi cambiare il tuo design per accettare IQueryable<string>
dove è necessario un string
.