Ленивая <T> реализация и дженерики.NET

Я искал способы сделать ленивую инициализацию и найденный 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' случай, конечно, сопровождаемый исключением на этапе выполнения, если вещи разлагаются.

15
задан Cristi Diaconescu 11 August 2010 в 22:18
поделиться

4 ответа

Я ожидаю, что встроенная реализация просто использует 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> { ... }
12
ответ дан 1 December 2019 в 03:34
поделиться

Почему бы вам просто не загрузить параллельные расширения и не установить Lazy для 3.5? Прямая ссылка

2
ответ дан 1 December 2019 в 03:34
поделиться

Вы можете использовать статический фабричный метод вместо перегрузки конструктора:

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() { ... }
}
7
ответ дан 1 December 2019 в 03:34
поделиться

Мой вопрос: могу ли я извлечь максимум из оба мира?

Нет.

По сути, у вас нет проверяемого ограничения времени компиляции.

0
ответ дан 1 December 2019 в 03:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: