Search code examples
c#lazy-loadinglazy-evaluation

Why data in Lazy Loading C# is loaded before I need it?


In one website I read:

Lady loading is the concept of delaying the loading of an object until we need this data. In other words, we load the object on demand, rather than unnecessarily loading data earlier.

I want to try using Lazy Loading in my simple console app.

Student Class

 public class Students
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public string PESEL { get; set; }

        private Lazy<List<Students>> LazyStudents;
        

        public Students()
        {
           
        }

        public List<Students> StudentsProperty
        {
            get
            {
                if (LazyStudents == null)
                    LazyStudents = new Lazy<List<Students>>(() => LoadStudensList());
                return LazyStudents.Value;
            }
        }

        public List<Students> LoadStudensList()
        {
            List<Students> tempStudentsList = new List<Students>();
            tempStudentsList.Add(new Students() { Name = "Adam", Surname = "Wróbel", PESEL = "96120904999" });
            tempStudentsList.Add(new Students() { Name = "Edyta", Surname = "Urbańczyk", PESEL = "76354736458" });
            tempStudentsList.Add(new Students() { Name = "Gabriela", Surname = "Rydwańska", PESEL = "72637284923" });
            tempStudentsList.Add(new Students() { Name = "Dawid", Surname = "Rytel", PESEL = "62736482732" });

            return tempStudentsList;
        }
    }

Program and Main() method:

 class Program
    {
        static void Main(string[] args)
        {
            Students students = new Students();
            
            Console.WriteLine("Hello World!");
            foreach (var items in students.StudentsProperty)
            {
                    Console.WriteLine($"Imię: {items.Name}");
                    Console.WriteLine($"Nazwisko: {items.Surname}");
                    Console.WriteLine($"PESEL: {items.PESEL}");
                    Console.WriteLine("");
            }

            Console.WriteLine("Hello World!");
        }
    }

I suppose that i use Lazy Loading, when i create new object of Students class StudentsProperty (return elements from Lazy List) still empty. Elements to LazyList will be added when i use method using StudentsProperty

 foreach (var items in students.StudentsProperty)
            {
                    Console.WriteLine($"Imię: {items.Name}");
                    Console.WriteLine($"Nazwisko: {items.Surname}");
                    Console.WriteLine($"PESEL: {items.PESEL}");
                    Console.WriteLine("");
            }

I Add breakpoint before foreach loop and i see that StudentsProperty has elements: enter image description here

Have I implemented lazy loading incorrectly or do I understand something wrong?


Solution

  • Lazy.Value is when the lazy gets evaluated.

      LazyStudents = new Lazy<List<Students>>(() => LoadStudensList());
      return LazyStudents.Value;
    

    doesn't make any sense. In the first line you say "evaluate when it's needed" and the second line says "I need it now".

    By null checking you've did all the lazy evaluation on your own. Use either this:

        private Lazy<List<Students>> LazyStudents = new Lazy<List<Students>>(() => LoadStudensList());
    
        public List<Students> StudentsProperty
        {
            get
            {
                return LazyStudents.Value;
            }
        }
    

    or this:

        private List<Students> LazyStudents;
    
        public List<Students> StudentsProperty
        {
            get
            {
                if (LazyStudents == null)
                    LazyStudents = LoadStudensList();
                return LazyStudents;
            }
        }
    

    They're supposed to be conceptually the same thing. Except multithreading safeguards, debugging and probably other pesky details, that make no difference most of the time.

    The issue why you see values in debugger is bit more complicated. Looking at the Lazy source, it should display null in debugger, thanks to internal T ValueForDebugDisplay. However, you're NOT looking at value of lazy. By looking at at Students.StudentsProperty you're looking at your get and debbuger evaluates getters and your getter forces Lazy to evaluate. You've bypassed all that debugging code that MS had created for us. Expose the Lazy directly, it's meant to be. Or, instead of debugger, put a log in LoadStudensList()

    Bottom line: Lazy is lazy only until Value gets called.