2010-07-08 2 views
22

.NET 4.0 имеет класс полезности System.Lazy, который выполняет инициализацию ленивого объекта. Я хотел бы использовать этот класс для проекта 3.5. Однажды я увидел реализацию где-то в ответе stackoverflow, но я больше не могу ее найти. Есть ли у кого-то альтернативная реализация Lazy? Ему не нужны все функции безопасности потоков версии 4.0.Реализация Lazy <T> для .NET 3.5

Обновлен:

ответы содержат ноны потокобезопасные и поточно версию.

ответ

25

Вот реализация, которую я использую.

/// <summary> 
/// Provides support for lazy initialization. 
/// </summary> 
/// <typeparam name="T">Specifies the type of object that is being lazily initialized.</typeparam> 
public sealed class Lazy<T> 
{ 
    private readonly object padlock = new object(); 
    private readonly Func<T> createValue; 
    private bool isValueCreated; 
    private T value; 

    /// <summary> 
    /// Gets the lazily initialized value of the current Lazy{T} instance. 
    /// </summary> 
    public T Value 
    { 
     get 
     { 
      if (!isValueCreated) 
      { 
       lock (padlock) 
       { 
        if (!isValueCreated) 
        { 
         value = createValue(); 
         isValueCreated = true; 
        } 
       } 
      } 
      return value; 
     } 
    } 

    /// <summary> 
    /// Gets a value that indicates whether a value has been created for this Lazy{T} instance. 
    /// </summary> 
    public bool IsValueCreated 
    { 
     get 
     { 
      lock (padlock) 
      { 
       return isValueCreated; 
      } 
     } 
    } 


    /// <summary> 
    /// Initializes a new instance of the Lazy{T} class. 
    /// </summary> 
    /// <param name="createValue">The delegate that produces the value when it is needed.</param> 
    public Lazy(Func<T> createValue) 
    { 
     if (createValue == null) throw new ArgumentNullException("createValue"); 

     this.createValue = createValue; 
    } 


    /// <summary> 
    /// Creates and returns a string representation of the Lazy{T}.Value. 
    /// </summary> 
    /// <returns>The string representation of the Lazy{T}.Value property.</returns> 
    public override string ToString() 
    { 
     return Value.ToString(); 
    } 
} 
+0

Две проблемы, которые у меня возникают с этим: Прежде всего, предпочтительно блокировать частный объект, чем' lock (this) ', поскольку вы не можете контролировать, кто else может заблокировать ваш экземпляр «Lazy». Во-вторых, я не думаю, что создание 'isValueCreated' поля' volatile' служит для любой цели, когда вы уже используете критический раздел (не так ли? Исправьте меня, если я ошибаюсь). – Aaronaught

+0

Я согласен, что volatile используется, когда блокировка не используется. Из MSDN: изменчивый модификатор обычно используется для поля, к которому обращаются несколько потоков, без использования оператора блокировки для сериализации доступа. Использование изменчивого модификатора гарантирует, что один поток извлекает самое современное значение, написанное другим потоком. –

+0

Я изменил ответ. –

10

Если вам не нужна безопасность нити, ее можно легко поставить вместе с заводским методом. Я использую один очень похожее на следующее:

public class Lazy<T> 
{ 
    private readonly Func<T> initializer; 
    private bool isValueCreated; 
    private T value; 

    public Lazy(Func<T> initializer) 
    { 
     if (initializer == null) 
      throw new ArgumentNullException("initializer"); 
     this.initializer = initializer; 
    } 

    public bool IsValueCreated 
    { 
     get { return isValueCreated; } 
    } 

    public T Value 
    { 
     get 
     { 
      if (!isValueCreated) 
      { 
       value = initializer(); 
       isValueCreated = true; 
      } 
      return value; 
     } 
    } 
} 
+0

Любой, кто мог бы скопировать это: здесь легко может быть путаница закрытия с инициализатором. Не забудьте зафиксировать свои ценности! –

+0

@Rex: Вы имеете в виду, если вы инициализируете экземпляр 'Lazy ' из цикла? Использует ли 'System.Lazy' какую-нибудь магию, чтобы обойти обычные опасности захвата? – Aaronaught

+0

Петля - хороший пример. Я сомневаюсь, что настоящий «Lazy» отличается от меня, хотя я не уверен. Просто подумал, что я хотел бы указать на это, потому что я вижу, что многие люди попадают в неприятности с такой моделью. –

2

Несколько упростить версию

public class Lazy<T> where T : new() 
{ 
    private T value; 

    public bool IsValueCreated { get; private set;} 

    public T Value 
    { 
    get 
    { 
     if (!IsValueCreated) 
     { 
      value = new T(); 
      IsValueCreated = true; 
     } 
     return value; 
    } 
    } 
} 
+0

Требуется значение по умолчанию ctor –

+4

@BC: Да, это то, что означает 'where T: new()'. –

-1

Некоторые смешные (но не очень полезной) материал может быть добавлен Ааронову: неявной Coversion от делегата:

public static implicit operator Lazy<T>(Func<T> initializer) 
{ 
    return new Lazy<T>(initializer); 
} 

А использование

private static Lazy<int> Value = new Func<int>(() => 24 * 22); 

У компилятора C# есть некоторые проблемы с выполнением этого преобразования, например, назначение выражения лямбда не работает, но это еще одна причина, по которой ваши коллеги думают немного :)

+0

lambdas может быть либо деревьями выражений, либо делегатами, поэтому компилятор отказывается считать их такими или другими. та же причина, почему вы не можете поместить лямбду в var. На самом деле раздражает иногда ... –

+0

Вы правы, но этот код не работает даже с методами: ' public static int NewValue() {возвращение 24 * 15; } общественный статический Lazy V = NewValue; // Ошибка компиляции, требуется новый Func (NewValue) ' – STO

Смежные вопросы