1

I'm trying to do somethings have three things:

  • Property is auto setter/getter (required)
  • Property is lazy loading (required)

How can I have lazy loading for C# automatic properties?

To make two things above, I think I need to use Reflection (or maybe other). This is what I have tried so far. I have a object test:

public class MyClass
{
    private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() => new MyObjectClass());
    public MyObjectClass MyObject { get { return lazyObjectClass.Value; } }
}

Normally, the MyObjectClass will be initialize when MyObject called.

But, I want to auto-initialize MyObjectClass. So, I change the MyClass to like this:

public class MyClass
{
    public MyClass()
    {
        //Get field
        var field = this.GetType().GetField("lazyObjectClass", BindingFlags.NonPublic | BindingFlags.Instance);
        //Get value of field
        Lazy<MyObjectClass> lazyValue = (Lazy<MyObjectClass>)field.GetValue(this);

        //Get property MyObject
        var property = this.GetType().GetProperty("MyObject");
        //Set value to the MyObject
        property.SetValue(this, lazyValue.Value); // <- when It called, MyObjectClass is ititialize too
    }
    private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() => new MyObjectClass());
    public MyObjectClass MyObject { get; set; }
}

But, when I SetValue to MyObject by Reflection, MyObjectClass is initialize too. How can I fix that or let me know if I should use a totally different solution.

Reza Aghaei
  • 103,774
  • 12
  • 145
  • 300
mikenlanggio
  • 646
  • 1
  • 4
  • 16
  • What do you mean? – mikenlanggio Nov 28 '19 at 08:19
  • I want to make somethings.., I just need to define `public MyObjectClass MyObject { get; set; }`, and it's equivalent to `public MyObjectClass MyObject { get{ return lazyObjectClass.Value; } set; }`. That's it! – mikenlanggio Nov 28 '19 at 09:01
  • So, you want to initialize your property from the constructor? Why make a Lazy field to begin with then? – RMH Nov 29 '19 at 17:42
  • @RMH I want my property is Lazy loading. It's mean, `MyObjectClass` just initialize when it called. – mikenlanggio Dec 02 '19 at 07:05
  • @RezaAghaei I want write property like this `public MyObjectClass MyObject { get; set; }`. And when it called, `MyObjectClass` will be initialize. Why no one get that? :( – mikenlanggio Dec 03 '19 at 02:06
  • OK, This is what I get from your question: you want to have a property having both getter and setter and you want to do lazy loading for the getter (if required). – Reza Aghaei Dec 03 '19 at 05:23
  • And `Reflection` is required – mikenlanggio Dec 03 '19 at 07:04
  • Why? You're not doing this lazily. You're using reflection to set the value of the property in the constructor. Please tell us what you expect to behave different from just doing `MyObject = new MyObjectClass();`, because your current code is doing exactly this in a roundabout way. – Lasse V. Karlsen Dec 03 '19 at 08:48
  • If you want to have a lazily provided getter you cannot do `{ get; set; }`, without using some kind of AOP library like PostSharp to rewrite the code after compilation. – Lasse V. Karlsen Dec 03 '19 at 08:49
  • 1
    You cannot have lazy loading for auto-propertes unless there is a context which creates a proxy over your class and handle lazy loading. For example in EF you can have lazy loading for virtual automatic properties, but the thing is, objects are behind a proxies there. I am sure you don't need to go to that direction by creating proxy. – Reza Aghaei Dec 03 '19 at 08:54
  • 2
    If what you are trying to do is for learning purpose, it's not a good example for neither learning reflection nor lazy loading. If you are trying to solve a specific business problem and looking for the solution, don't say it should be automatic property and should use reflection. Try to understand the business requirement and solve the problem. In general, you should stop considering reflection and auto properties in this context, unless you have some proxy generator and interceptors. – Reza Aghaei Dec 03 '19 at 09:02

2 Answers2

2

Short Answer

You may not like it, but you cannot have lazy loading for auto-implemented properties u̲n̲l̲e̲s̲s̲:

  • You have container/factory which creates a proxy over your class and handle lazy loading by the proxy.
  • Or You have some mechanism like using a weaving tool to manipulate the generated IL at build time and add the lazy-loading feature to your class.

I doubt you really need lazy-loading with auto-implemented properties. So my advise is: Change your code and forget about auto-implemented properties and easily use lazy loading using custom getter and setter (with or without using lazy<T>.

Anyway, if you are curious to know more, read the detailed answer.

Note: By doing some research, you may find some posts in stackoverflow which are trying to address similar questions, for example: this post, or this one. I've tried to share more details/options here in this post. You may also find the linked posts useful.

Detailed Answer - Lazy Loading for Auto-Implemented Properties

You want to have Lazy Initialization for Auto-Implemented Property in your class.

As it's already mentioned in the short answer, you cannot have lazy loading for auto-implemented properties unless:

  • You have container/factory which creates a proxy over your class and handle lazy loading by the proxy.
  • Or You have some mechanism like using a weaving tool to manipulate the generated IL at build time and add the lazy-loading feature to your class.

For example in Entity Framework you can have lazy loading for virtual automatic properties. But the thing is, it's handled by Entity Framework and in this case it creates proxy for the object.

Lazy Loading for Auto-Implemented Properties - Options

To implement one of the above solutions, you can use following options:

To extend the solution you can use the following ideas to generate the proxy and handling lazy-loading in the proxy:

  • Implement proxy class yourself: You can create a proxy class derived from your main class and then override the property which you want load lazily. Then you can register the derived class in your DI container and return an instance of the derived class whenever you as instance of the main class requested.

  • Implement a Generic Factory to create proxy at run-time using Reflection.Emit: You can create a generic factory method to create the proxy at run-time using Reflection.Emit or other run-time assembly generation mechanisms.

  • Implement a Generic Factory to create proxy at run-time using RealProxy You can create a generic factory and use RealProxy and intercept the getter and setter of the property and add lazy loading feature to the property of the proxy.

  • Rely on intercepting features of DI frameworks: You can use intercepting features of your DI Framework (if it has such feature) and intercept the get and set and implement lazy loading. For example you may be able to implement the feature using Interceptors in Unity.

  • Rely on weaving tools to manipulate the assembly at build time: You can relay on AOP Framework and Weaving tools like Fody or PostSharp to manipulate the IL of your assembly and make the property lazy-loadable at compile time. For example you can find an implementation for PostSharp here.

Reza Aghaei
  • 103,774
  • 12
  • 145
  • 300
  • Thank for your answer. But It's not my answer I want. I want `MyInstance` just like this: `public MyClass MyInstance{get;set;}` and other part must be do by `Reflection`! – mikenlanggio Dec 03 '19 at 07:00
  • I just update my question. Hope everyone can get that. – mikenlanggio Dec 03 '19 at 07:15
  • 1
    *I like to use reflection* is not a requirement, reflection may be or may not be part of the solution. *I like to use auto-property setter/getter* is not a requirement as well, auto-property is just a sugar for cases it may be useful. You first need to confirm the requirement: "This is what I get from your question: you want to have a property having both get and set and you want to do lazy loading in the get". – Reza Aghaei Dec 03 '19 at 08:45
  • 3
    The other thing is, you cannot have lazy loading for auto-propertes unless there is a context which creates a proxy over your class and handle lazy loading. For example in EF you can have lazy loading for virtual automatic properties, but the thing is, objects are behind a proxies there. I am sure you don't need to go to that direction by creating proxy. – Reza Aghaei Dec 03 '19 at 08:52
0

The problem is related to the get method.

In first case you have:

public class MyClass
{
    private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() => new MyObjectClass());

    public MyObjectClass MyObject { get { return lazyObjectClass.Value;** } }
}

When you access MyObject, in fact you call a get method that reads from your lazyObjectClass, so lazyObjectClass is evalued only when it is called.

In the second case, you use an autoproperty, but real code is like this:

public class MyClass
{
    private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() => new MyObjectClass());

    private MyObjectClass <MyObject>k__BackingField;
    public MyObjectClass MyObject 
    { 
       get 
       {
          return <MyObject>k__BackingField;
       }
       set
       {
          <MyObject>k__BackingField = value;
       }
    }
}

But there is a problem, get method reads directly from hidden autogenerated k__BackingField that must already been evalueted, there is no call to lazy, and you can't change get implementation with reflection.

There is no way to do what you are thinking, but here I suggest this workaround:

public class MyObjectClass
    {

    }

  public class MyClass
    {
        public MyClass()
        {
            var field = this.GetType().GetField("lazyObjectClass", BindingFlags.NonPublic | BindingFlags.Instance);
            //Get value of field
            Lazy<MyObjectClass> lazyValue = (Lazy<MyObjectClass>)field.GetValue(this);

            //Get property MyObject
            var lazyField = this.GetType().GetField("_lazy", BindingFlags.NonPublic | BindingFlags.Instance);
            lazyField.SetValue(this, lazyValue);

        }

        private Lazy<MyObjectClass> _lazy = null;
        private MyObjectClass myValue = null;

        private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() =>
        {
            return new MyObjectClass();
        });
        public MyObjectClass MyObject
        {
            get
            {
                if (myValue == null)
                {
                    return _lazy.Value;
                }
                else
                {
                    return myValue;
                }

            }
            set
            {
                myValue = value;
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = new MyClass();

            var a = myClass.MyObject;
        }
    }
Stefano Balzarotti
  • 1,542
  • 1
  • 11
  • 29