2010-07-08 26 views
22

.NET 4.0 có lớp tiện ích tốt được gọi là System.Lazy làm khởi tạo đối tượng lười. Tôi muốn sử dụng lớp này cho một dự án 3.5. Một lần tôi nhìn thấy một thực hiện ở đâu đó trong một câu trả lời stackoverflow nhưng tôi không thể tìm thấy nó nữa. Có ai có thực hiện thay thế Lazy? Nó không cần tất cả các tính năng an toàn của phiên bản 4.0 của khung công tác.Thực hiện Lazy <T> cho .NET 3.5

Cập nhật:

Answers chứa a thread không an toàn và một phiên bản đề an toàn.

Trả lời

25

Đây là một thực hiện mà tôi sử dụng.

/// <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

Hai vấn đề tôi có với điều này: Trước hết, nên' khóa' một đối tượng riêng tư hơn là 'khóa (this)', vì bạn không thể kiểm soát ai khác có thể khóa trên ví dụ 'Lazy' của bạn. Thứ hai, tôi không nghĩ rằng việc tạo ra 'isValueCreated' là một trường' volatile' phục vụ bất kỳ mục đích gì khi bạn đã sử dụng một phần quan trọng (đúng không? Sửa chữa nếu tôi sai). – Aaronaught

+0

Tôi đồng ý dễ bay hơi được sử dụng khi khóa không được sử dụng. Từ MSDN: Bộ sửa đổi dễ bay hơi thường được sử dụng cho một trường được truy cập bởi nhiều luồng mà không sử dụng lệnh khóa để truy cập tuần tự. Sử dụng công cụ sửa đổi dễ bay hơi, đảm bảo rằng một chuỗi truy lục giá trị cập nhật mới nhất được viết bởi một chuỗi khác. –

+0

Tôi đã sửa đổi câu trả lời. –

10

Nếu bạn không cần an toàn chỉ, bạn có thể dễ dàng đặt cùng với phương pháp nhà máy. Tôi sử dụng một trong rất tương tự như sau:

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

Bất kỳ ai có thể sao chép nội dung này: Có thể dễ nhầm lẫn với trình khởi tạo tại đây. Đảm bảo nắm bắt các giá trị của bạn! –

+0

@Rex: Ý của bạn là, nếu bạn đang khởi tạo phiên bản 'Lazy ' từ một vòng lặp? Liệu 'System.Lazy' có thực hiện một số phép thuật để vượt qua những nguy hiểm bình thường trong việc bắt giữ không? – Aaronaught

+0

một vòng lặp là một ví dụ tốt. Tôi nghi ngờ nếu thực sự 'Lazy' là bất kỳ khác nhau, mặc dù tôi không chắc chắn. Chỉ nghĩ rằng tôi sẽ chỉ ra nó bởi vì tôi thấy rất nhiều người gặp rắc rối với kiểu mẫu này. –

2

Một điều khá đơn giản hóa phiên bản của

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

Yêu cầu ctor mặc định –

+4

@BC: Có, đó là ý nghĩa của 'where T: new()'. –

-1

Một số hài hước (nhưng không phải là rất có thể sử dụng) công cụ có thể được thêm aaron của: coversion ngầm từ đại biểu:

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

Và sử dụng

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

Trình biên dịch C# có một số vấn đề với việc thực hiện chuyển đổi này, ví dụ gán biểu thức lambda không hoạt động, nhưng một điều nữa khiến cho các đồng nghiệp của bạn suy nghĩ một chút :)

+0

lambdas có thể là cây biểu thị hoặc đại biểu để trình biên dịch từ chối coi chúng là một hoặc cái kia. cùng một lý do tại sao bạn không thể đặt một lambda trong một var. Đôi khi thực sự khó chịu ... –

+0

Bạn nói đúng, nhưng mã này không hoạt động ngay cả với các phương pháp: ' static static int NewValue() { return 24 * 15; } tĩnh công cộng Lazy V = NewValue; // Lỗi biên dịch, cần Func mới (NewValue) ' – STO