2010-10-27 33 views
63

Trong C#,C# Lazy tải tự động thuộc tính

Có cách nào để biến thuộc tính tự động thành thuộc tính tự động được nạp lười với giá trị mặc định được chỉ định không?

Về cơ bản, tôi đang cố gắng để tắt chức năng này ...

private string _SomeVariable 

public string SomeVariable 
{ 
    get 
    { 
      if(_SomeVariable == null) 
      { 
      _SomeVariable = SomeClass.IOnlyWantToCallYouOnce(); 
      } 

      return _SomeVariable; 
    } 
} 

vào một cái gì đó khác nhau, nơi tôi có thể xác định các mặc định và nó xử lý phần còn lại tự động ...

[SetUsing(SomeClass.IOnlyWantToCallYouOnce())] 
public string SomeVariable {get; private set;} 
+0

@Gabe: Lưu ý rằng lớp học sẽ chỉ được gọi một lần nếu nó không bao giờ urns null. – RedFilter

+0

Tôi phát hiện ra rằng ... nó có vẻ là sử dụng các mô hình singleton – ctorx

Trả lời

80

Không có. Thuộc tính được tự động triển khai chỉ hoạt động để thực hiện các thuộc tính cơ bản nhất: trường sao lưu với getter và setter. Nó không hỗ trợ kiểu tùy chỉnh này.

Tuy nhiên bạn có thể sử dụng 4,0 Lazy<T> loại để tạo ra mô hình này

private Lazy<string> _someVariable =new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce); 
public string SomeVariable { 
    get { return _someVariable.Value; } 
} 

Mã này uể oải sẽ tính toán giá trị của _someVariable lần đầu tiên khái niệm Value được gọi. Nó sẽ chỉ được tính một lần và sẽ lưu lại giá trị để sử dụng trong tương lai của tài sản Value

+1

Trên thực tế, có vẻ như tôi như Lazy thực hiện mô hình singleton. Đó không phải là mục tiêu của tôi ... mục tiêu của tôi là tạo ra một tài sản được tải lười biếng mà được tạo ra một cách lười biếng nhưng được xử lý cùng với thể hiện của lớp mà nó đang sống. Lười biếng dường như không được thực hiện theo cách đó. – ctorx

+12

@ctorx Lười biếng không có gì để làm với mẫu đơn. Nó thực hiện chính xác những gì bạn muốn nó làm. – Stijn

+4

Lưu ý, 'SomeClass.IOnlyWantToCallYouOnce' trong ví dụ của bạn phải tĩnh để được sử dụng với trình khởi tạo trường. –

2

tôi don 't nghĩ rằng điều này là có thể với C# tinh khiết. Nhưng bạn có thể làm điều đó bằng cách sử dụng một IL rewriter như PostSharp. Ví dụ, nó cho phép bạn thêm các trình xử lý trước và sau các hàm tùy thuộc vào các thuộc tính.

5

Không như vậy, tham số cho thuộc tính phải không đổi về giá trị, bạn không thể gọi mã (thậm chí là mã tĩnh).

Tuy nhiên, bạn có thể thực hiện điều gì đó với các khía cạnh của PostSharp.

Kiểm tra xem chúng ra:

PostSharp

17

Có lẽ ngắn gọn nhất mà bạn có thể nhận được là sử dụng các nhà điều hành null-coalescing:

get { return _SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); } 
+4

Trong trường hợp 'IOnlyWantToCallYouOnce' trả về' null', nó sẽ gọi nó nhiều hơn một lần. – JaredPar

+6

Khi sử dụng toán tử kết hợp null, ví dụ trên sẽ thất bại. Cú pháp chính xác là: '_SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); '- chú ý việc thêm dấu ngoặc đơn xung quanh thiết lập' _SomeVariable' nếu nó là null. –

4

Dưới đây là thực hiện của tôi về một giải quyết cho vấn đề của bạn. Về cơ bản ý tưởng là một thuộc tính sẽ được thiết lập bởi một hàm ở lần truy cập đầu tiên và các truy cập tiếp theo sẽ mang lại giá trị trả về giống như giá trị đầu tiên.

public class LazyProperty<T> 
{ 
    bool _initialized = false; 
    T _result; 

    public T Value(Func<T> fn) 
    { 
     if (!_initialized) 
     { 
      _result = fn(); 
      _initialized = true; 
     } 
     return _result; 
    } 
} 

Sau đó, để sử dụng:

LazyProperty<Color> _eyeColor = new LazyProperty<Color>(); 
public Color EyeColor 
{ 
    get 
    { 
     return _eyeColor.Value(() => SomeCPUHungryMethod()); 
    } 
} 

Có tất nhiên các chi phí đi qua các con trỏ hàm xung quanh, nhưng nó không được công việc đối với tôi và tôi không để ý quá nhiều chi phí so với chạy các phương pháp hơn và hơn nữa.

+0

Sẽ không có ý nghĩa hơn khi đưa hàm này cho hàm tạo? Bằng cách này, bạn sẽ không tạo nội dung đó mỗi lần và bạn có thể vứt bỏ nó sau khi bạn sử dụng nó lần đầu tiên. –

+0

@ lund.mikkel yeah, điều đó cũng sẽ hoạt động. Có thể là trường hợp sử dụng cho cả hai cách tiếp cận. – deepee1

+5

Nếu bạn chuyển hàm cho hàm tạo, giống như lớp Lazy .Net, thì hàm được truyền vào sẽ phải tĩnh, tôi biết điều này không phù hợp với thiết kế của tôi trong nhiều trường hợp. – crunchy

8

Có một tính năng mới trong C# 6 gọi Expression Bodied Auto-Properties, cho phép bạn để viết nó một chút bụi:

public class SomeClass 
{ 
    private Lazy<string> _someVariable = new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce); 

    public string SomeVariable 
    { 
     get { return _someVariable.Value; } 
    } 
} 

Bây giờ có thể được viết như sau:

public class SomeClass 
{ 
    private Lazy<string> _someVariable = new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce); 

    public string SomeVariable => _someVariable.Value; 
} 
+0

Trong phần cuối của mã, khởi tạo không thực sự là lười biếng. 'IOnlyWantToCallYouOnce' sẽ được gọi trong khi xây dựng mỗi khi lớp được khởi tạo. –

+0

@TomBlodget Cảm ơn bạn, bạn đang phải –

+0

Vì vậy, trong otherwords này không phải là lười biếng nạp? – Zapnologica

0

https://github.com/bcuff/AutoLazy sử dụng Fody để cung cấp cho bạn một cái gì đó như thế này

public class MyClass 
{ 
    // This would work as a method, e.g. GetSettings(), as well. 
    [Lazy] 
    public static Settings Settings 
    { 
     get 
     { 
      using (var fs = File.Open("settings.xml", FileMode.Open)) 
      { 
       var serializer = new XmlSerializer(typeof(Settings)); 
       return (Settings)serializer.Deserialize(fs); 
      } 
     } 
    } 

    [Lazy] 
    public static Settings GetSettingsFile(string fileName) 
    { 
     using (var fs = File.Open(fileName, FileMode.Open)) 
     { 
      var serializer = new XmlSerializer(typeof(Settings)); 
      return (Settings)serializer.Deserialize(fs); 
     } 
    } 
} 
Các vấn đề liên quan