C # 4 Lazy Loading & Lazy <T>
I have a cart model model that has a List property like this:
public List<CartItem> CartItems { get { if (_cartItems == null) _cartItems = Services.CartItemService.GetCartItems(); return _cartItems; } } private List<CartItem> _cartItems;
This is normal if the service that used the data query from SQL Server does not return null, in which case the database can be unnecessarily deleted several times using the CartItems link. Then I noticed that Lazy<T>
is available to me, so I tried to change my code a bit (since Lazy<T>
takes into account null and prevents multiple database calls)
public List<CartItem> CartItems { get { return _cartItems.Value; } } private Lazy<List<CartItem>> _cartItems = new Lazy<List<CartItem>>(() => { // return Services.CartItemService.GetCartItems(); cannot be called here :( });
Compile time error
"Field initializer cannot reference non-static fields, method or property"
Services is a public property in the same class as CartItems, but I cannot figure out if it is possible to access the Func<List<CartItem>>
. I donโt want to create factory classes for each property - I have something similar for us in many places, and I want to be ... well .... lazy.
You can create a field in the constructor. He can also pay for porting a service call to his own method. I.e
private readonly Lazy<List<CartItem>> _cartItems; public MyClass() { _cartItems = new Lazy<List<CartItem>>(GetCartItems); } public List<CartItem> GetCartItems() { return Services.CartItemService.GetCartItems(); }
To answer your question in a comment:
I am now interested in knowing why this works in the constructor, and not in my example.
The order of building a C # object is as follows. First, all field initializers are executed in order from most to least derived class. Therefore, if you have a class B and a derived class D, and you create a new D, then the initializers of the fields D are all executed before any initializer of the field B.
As soon as all field initializers are started, the constructors work in order from the lowest value obtained to most derivatives. That is, the body of constructor B is executed first, then the body of constructor D is executed.
This may seem strange, but the argument is simple:
First of all, it is obvious that ctor base classes must be executed before derived ctor classes. The deduced ctors may depend on the state initialized by the ctor base class, but the opposite is unlikely to be true; the base class is usually not aware of the derived class.
Secondly, it is obvious that the initialized fields will have their own values โโbefore the operations on the design bodies are performed. It would be very strange if the constructor observed an uninitialized field when there is a field initializer there.
So the way to generate ctors code is that each ctor follows a pattern: "Initialize my fields, then call my base class ctor, and then execute my ctor." Since everyone follows this pattern, all fields are initialized in order from the derivative to the base, and all ctor bodies are executed from the base to the derivative.
So now that we have set this up, what happens when the field initializer refers to "this" both explicitly and implicitly? Why would you do that? "this" is likely to be used to access a method, field, property or event for an object whose field initializers are not all running, and none of the constructor bodies are running! Clearly, this is incredibly dangerous. Most of the state required for the class to work properly is still missing.
Therefore, we prohibit any reference to "this" in any field initializer.
When you get to a certain constructor body, you know that all field initializers are running and all constructors for all base classes are running. You have much more evidence that the object is probably in good condition, so access to "this" is allowed in the body of the constructor. (You can still do stupidly dangerous things, for example, you can call a virtual method overridden in a derived class when inside the constructor of the base class, the derived constructor is not already running, and the derived method may fail as a result. Be careful!)
Now you can very well say that there is a big difference between:
class D : B { int x = this.Whatever(); // call a method on the base class, whose ctor has not run!
and
class D : B { Func<int> f = this.Whatever;
or similarly:
class D : B { Func<int> f = ()=>this.Whatever();
It doesnโt say anything. It does not read a state that can be uninitialized. Clearly, this is completely safe. We could make a rule that says: "Allow this access in the field initializer when the access is in the delegation-group-method or lambda transform", right?
Nope. This rule allows you to shut down the security system:
class D : B { int x = ((Func<int>)this.Whatever)();
And we returned to the same boat again. Thus, we could create a rule that says: "Allow this access in the field analyzer, when the access is in the conversion of the delegation group method or lambda flow analyzer, can prove that the delegate is not called before the constructor starts." And now a team of language developers and teams of developers, developers, testing and training the compiler spend a huge amount of time, money and effort on solving a problem that we do not want to solve in the first place.
It is better for the language to have simple, understandable rules that increase security and can be correctly implemented, easily tested and clearly documented, than to have complex rules that allow working with obscure scenarios. Simple, secure rule An instance field initializer cannot have an explicit or implicit reference to 'this', period.
Further reading:
Section 10.4.5.2 of the C # language specification explicitly prohibits the use of this
in a field initializer:
The variable initializer for the instance field cannot reference the instance being created. Thus, this is a compile-time error for the
this
link in the variable initializer, since it is a compile-time error for the variable initializer to reference any instance of the element through a simple name.
The documentation for error CS0236 provides a constructor workaround recommended by others.