Я искал способы сделать ленивую инициализацию и найденный Lazy
который включен в.NET 4.
Я думал о прокрутке моей собственной реализации Lazy
для.NET 3.5 (с более простой политикой мультипотока), и я врезался в следующую проблему:
Ленивый имеет в основном два типа конструкторов:
class Lazy {
public Lazy(){...} // ctor #1
который использует конструктора по умолчанию T для создания экземпляра T, и
public Lazy(Func func){...} // ctor #2
который позволяет вызывающей стороне решить, как экземпляр T создается.
Теперь вот проблема:
Если я захочу время компиляции, проверяя на 1-й ctor, то я добавлю ограничение
class Lazy where T: new() {...}
на уровне класса. Это позволит мне использовать new T()
создать экземпляр; но это ограничение не необходимо для 2-го ctor и хуже, это также ограничивает типы, которые я могу использовать (тем, которые имеют значение по умолчанию ctor),
Если я захочу смочь использовать какой-либо тип с 2-м ctor, то я не установлю ограничения, и в 1-м ctor будет использовать отражение для проверки T
действительно имеет значение по умолчанию ctor. Этот подход, однако, испытает недостаток в проверке времени компиляции и только выдаст исключение на этапе выполнения, если 1-й ctor будет использоваться с неправильным типом.
Мой вопрос: я могу получить лучший из обоих миров?
Идеально, я хотел бы получить проверку времени компиляции на каждое использование ctor № 1, но в то же время смочь использовать ctor № 2 для типов, которые не имеют значения по умолчанию ctor.
Как реализация Microsoft делает это? (У меня с готовностью нет доступа к.NET 4 источниками или dlls).
Править: (После "луга Отражателя" блок MS)
Я проверил ссылочную реализацию, и она не делает проверок времени компиляции.
Это использует отражение для 'значения по умолчанию ctor' случай, конечно, сопровождаемый исключением на этапе выполнения, если вещи разлагаются.
Я ожидаю, что встроенная реализация просто использует Activator.CreateInstance
для простоты. Самый чистый способ, который я могу придумать, - это использовать отдельную фабрику:
// non-generic factory class with generic methods
public static class Lazy {
public static Lazy<T> Create<T>() where T : new() {
return Create<T>(() => new T());
}
public static Lazy<T> Create<T>(Func<T> ctor) { ... }
}
public class Lazy<T> { ... }
Почему бы вам просто не загрузить параллельные расширения и не установить Lazy
для 3.5? Прямая ссылка
Вы можете использовать статический фабричный метод вместо перегрузки конструктора:
public class Lazy<T>
{
public Lazy( Func<T> f ) { /*...*/ }
public static Lazy<R> Default<R>() where R : T, new()
{
return new Lazy<R>( () => new R() );
}
}
Теперь это нарушает совместимость (в некоторой степени) с версия .NET 4.0 Lazy
, но она обеспечивает безопасность времени компиляции для обоих типов использования.
Вы можете сделать это немного чище, сделав конструкторы для Lazy
защищенными внутренними и предоставив статический фабричный класс, который вы всегда используете для создания экземпляров:
public static class Lazy {
static Lazy<T> Create<T>( Func<T> ) { ... }
static Lazy<T> Create<T>( ) where T : new() { ... }
}
Мой вопрос: могу ли я извлечь максимум из оба мира?
Нет.
По сути, у вас нет проверяемого ограничения времени компиляции.