Как я мог реализовать свой собственный механизм отложенного выполнения в С#?
Так, например, у меня есть:
string x = DoFoo();
Возможно ли выполнить некоторую магию, чтобы DoFoo не выполнялся до тех пор, пока я не "использовал" x?
Как я мог реализовать свой собственный механизм отложенного выполнения в С#?
Так, например, у меня есть:
string x = DoFoo();
Возможно ли выполнить некоторую магию, чтобы DoFoo не выполнялся до тех пор, пока я не "использовал" x?
Вы можете использовать lambdas/delegates:
Func<string> doit = () => DoFoo();
// - or -
Func<string> doit = DoFoo;
Позже вы можете вызвать doit
так же, как метод:
string x = doit();
Я думаю, что самое близкое, что вы можете получить, это примерно так:
Lazy<string> x = DoFoo;
string y = x; // "use" x
С определением Lazy<T>
, аналогичным этому (untested):
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;
}
}
К сожалению, похоже, что алгоритмы определения типа компилятора не могут автоматически выводить тип Func<T>
и поэтому не могут сопоставлять его с оператором неявного преобразования. Нам нужно явно объявить тип делегата, что делает утверждения присваивания более подробными:
// 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);
Один из вариантов заключается в использовании класса Lazy<T>
, ранее из библиотеки параллельных расширений, теперь являющейся частью .NET Framework 4.0.
Он позволяет задерживать данные процесса в потоковом режиме.
Пока он несколько грязный, вы всегда можете использовать ключевое слово 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());
}
Вместо передачи строки x передайте делегат, который закупает вам строку
Func<String> fooFunc=()=>DoFoo();
Почему бы просто не называть DoFoo() до тех пор, пока вы не захотите?
- Изменить
Я имею в виду, что вы имеете в виду "использовать"
Например, если вы хотите, чтобы он вызывался при вызове '.ToString()', вы всегда можете наследовать класс и реализовывать свою функцию там (но это было бы совсем неинтуитивно, ИМХО).
Вы в значительной степени описываете LINQ в действии. Запрос linq описывает, как получить данные, но данные извлекаются (вызывается DoFunc) только при повторении запроса. Подумайте, можете ли вы изменить свой дизайн, чтобы принять IQueryable<string>
, где вам нужен string
.