7

I have an object with List<>s exposed by properties. I generally initialize my lists within the property getter as follows:

public class Foo
{
    private List<bar> _barList;

    public List<bar>
    {
        get
        {
            if(_barList == null)
            {
                _barList = new List<Bar>()
            }

            return _barList;
        }

        set
        {
            _barList = value;
        }
    }

    public Foo()
    {
    }
}

However, my colleagues generally prefer initializing the list in the class constructor as follows:

public class Foo
{
    public List<bar> BarList { get; set; }

    public Foo()
    {
        BarList = new List<Bar>();
    }
}

Both cases prevent BarList from being accessed before it is initialized. The second seems more neat due to the use of autoproperties. The first seems like a better option though, since the list is only initialized when it is first used. Are there any other considerations I should take into account? Is there a best practice for this?

08Dc91wk
  • 4,254
  • 8
  • 34
  • 67
  • 2
    The important thing is ensuring the validity of your class's state. Both of these approaches yield the same result externally, but internally your lazy-initialised `_barList` might be null whereas the constructor initialised `BarList` won't be null when someone attempts to use it within the class. Well, externally the result isn't entirely the same if you start introducing multi-threaded behaviours into the equation. – Adam Houldsworth Jul 20 '15 at 09:47
  • 1
    It's "eager loading" vs. "lazy loading" approach really. – Y.S Jul 20 '15 at 09:47
  • 1
    In this case, It is basically a question of style (and opinion, though). In c# 6.0, you have another way of initializing an auto-implemented property, as [this SO answer](http://stackoverflow.com/a/40754/1389444) shows. – Patrik Jul 20 '15 at 09:48
  • @Patrik - thanks for pointing out the new autoproperty initialization feature of C# 6.0. I am using VS2015 RC so I will try it out! Could someone expand this into an answer? I am supposing this feature solves the internal access problem pointed out by @Adam? What are the memory implications of this (lazy vs eager)? – 08Dc91wk Jul 20 '15 at 11:08

2 Answers2

10

In addition to usrs answer, there is a another answer, using C# 6.0, on this question.

One of the new features is a initializer for auto-implemented properties. You'd write:

public class Foo
{
    public List<bar> BarList { get; set; } = new List<bar>();
}

Also, have a look at the new primary constructors, found a programmers.stackexchange article.

This initializer will be called on object-initialization. In matters of memory consumption/performance, it should be equal to initializing the property in the constructor.

Community
  • 1
  • 1
Patrik
  • 1,355
  • 12
  • 22
  • Using this feature, is the list initialized when the class is initialized, or does C# handle the "lazy" initialization for you? If it does, this seems like by far the best solution! – 08Dc91wk Jul 20 '15 at 11:49
  • Class-initialization-time (static constructor) is not possible because the property is an instance-member, so it must be object-initialization-time. – Patrik Jul 20 '15 at 11:50
  • Oops, I meant to say object, not class. So this feature seems equivalent to the second option in the OP.. hmm. Since in my case the list is sometimes not used, I'm wondering if the memory saving might make it worth writing it out myself. – 08Dc91wk Jul 20 '15 at 11:54
  • That discussion took in usrs answer place, I left it out since it'd be redundant. – Patrik Jul 20 '15 at 11:58
6

If you need to conserve memory the lazy strategy clearly is better. If that memory usage is not important the eager strategy results in a lot simpler code. It is preferable then.

In general it is a good thing to eliminate special cases. It's not a good thing that the list field can be null for internal access.

Note, that the code gets even shorter with C# 6 tilting the trade-off more towards the shorter code solution.

Note, that the lazy version is not thread safe for concurrent execution of the getter.

usr
  • 168,620
  • 35
  • 240
  • 369