0

I've got a scenario I discovered when checking code coverage on a class's properties. When the property is of type List< T >, and an initializer is used, the set method doesn't appear to be called. This isn't true of other types, like strings and ints. Code coverage doesn't show the set call, nor does a breakpoint in set get hit.

Example class:

public class ContainerClass
{
    public string Text { get; set; }
    public List<Item> Items { get; set; }
}

When using an initializer, like below, the set method on Text is called, and registers in code coverage, but the set method on Items does not, and I am wondering why:

var arrange = new ContainerClass
{
    Text = "value",
    Items = { new Item() }
};

Edit: I would point out that the list is correctly assigned, and can be tested against, but it appears to go around the actual set method.

Interestingly, when I specify the new list, it does get called:

var arrange = new ContainerClass
{
    Items = new List<Item> { new Item() }
};
Smack Jack
  • 257
  • 4
  • 16
  • 1
    the correct way to initialize it is in your last example, I am not sure what the other version means – bto.rdz Jun 17 '16 at 04:42
  • 3
    The list doesn't ever get assigned for me - resulting in a NullReferenceException. – BoltClock Jun 17 '16 at 04:43
  • 2
    §7.6.10.2 "A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the field or property, the elements given in the initializer are added to the collection referenced by the field or property." Your list is being initialized somewhere else in order for your code to work - the example you've given won't run correctly. – BoltClock Jun 17 '16 at 04:48
  • As @BoltClock's pointed out, my question didn't include the actual scenario I was using, and I had set the value on the property declaration with a new list. – Smack Jack Jun 17 '16 at 05:10

2 Answers2

3

When using an initializer, like below, the set method on Text is called, and registers in code coverage, but the set method on Items does not, and I am wondering why:

That's because the Items list is not actually being initialized at the time the collection initializer is called. From section 7.6.10.2 of the spec:

A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the field or property, the elements given in the initializer are added to the collection referenced by the field or property.

This means that the collection initializer assumes that the collection has already been instantiated (which is true even outside of an object initializer). Remember that a collection initializer is nothing more than a series of .Add() calls.

In order for your code to run without errors, Items must already have been initialized. This could have happened either in the property declaration itself (assuming it's an auto-implemented property):

public class ContainerClass
{
    public string Text { get; set; }
    public List<Item> Items { get; set; } = new List<Item>();
}

Or in the constructor (which you haven't shown):

public class ContainerClass
{
    public string Text { get; set; }
    public List<Item> Items { get; set; }

    public ContainerClass()
    {
        Items = new List<Item>();
    }
}

Otherwise, your code will throw a NullReferenceException as the program tries to evaluate the collection initializer.

Based on your statement that the setter never gets called for Items, it's likely it was initialized in the property declaration (or the backing field, if it's not actually an auto-implemented property). Had it been initialized in the constructor the setter would still have been called.

Your second example results in the setter being called because you are indeed assigning a new list to Items there.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • You are correct, I missed that I had included the = new List() after the property declaration. Your explanation makes perfect sense, thank you. – Smack Jack Jun 17 '16 at 05:09
0

You could do either:

public class ContainerClass
{
    public string Text { get; set; }
    public List<Item> Items { get; set; }

    public ContainerClass()
    {
        Items = new List<Item>();
    }
}

Then:

var arrange = new ContainerClass
{
    Text = "SomeValue"
};
arrange.Items.Add(new Item(){Prop=Value});

Or, if you don't use the constructor as stated, you can initialize a list like so:

var arrange = new ContainerClass
{
    Items = new List<Item>(){ new Item(){Prop=Value} }
};
Phil
  • 294
  • 2
  • 11
  • Initializing within the constructor is enough for the first initialization example in the question to work, there is no need to modify it. The and/or example is just a restatement of the second example in the question. – BoltClock Jun 17 '16 at 04:54
  • That's true, my example was meant for without the constructor. I'll update to reflect it. – Phil Jun 17 '16 at 04:55