5

I have a class with a get-only collection property. I would like to initialize the collection with the values from an existing collection.

I know that it is possible to initialize the collection using a collection initializer. I could also create the object and then use AddRange on the collection to add the items of the existing collection. This would however create the object with an empty list and add the existing items afterwards.

Is there a way to create the object with the List properly initialized in the first place (without adding a constructor, of course)?

using System.Collections.Generic;

namespace EmptyConsoleApp
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            // Compiles, but is not what I need
            var firstHolder = new Holder()
            {
                TheList = {"A", "B"}
            };

            // Compiles, but initializes the list after object creation
            var existingList = new List<string>() {"Foo", "Bar"};
            var secondHolder = new Holder();
            secondHolder.TheList.AddRange(existingList);

            // Does not compile
            var thirdHolder = new Holder()
            {
                TheList = {existingList}
            };
        }
    }

    internal class Holder
    {
        public Holder()
        {
            TheList = new List<string>();
        }

        public List<string> TheList { get; }
    }
}
MaSiMan
  • 655
  • 6
  • 16
  • Well, it's a read only property. Why you expect that you can initialize it from outside without using a constructor? That's exactly why constructors exist – Tim Schmelter Apr 25 '18 at 08:14

3 Answers3

2

No. You can't assign this read-only property from a collection initializer. It is read-only after all.

TheList = { "A", "B" } works since it calls Add on TheList (once for each item added), it doesn't create and assign a new instance, which it is not allowed to.

TheList = { existingList } doesn't work since there is a typing issue (TheList = { existingList[0] } does work).

The best option you have it to create a constructor parameter and drop your idea of using collection initializers for something it isn't fit for.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • Thanks for the answer and explanation, if `Add` is called internally, it makes sense that you cannot use it with an existing list. – MaSiMan Apr 25 '18 at 08:17
  • Well, it does for `TheList = { existingList[0] }`. But that is an item from an existing list. – Patrick Hofman Apr 25 '18 at 08:18
  • You could add a method to the class that allows the initialization of the list if it is null (but that would still require a private setter for the property) – H.J. Meijer Apr 25 '18 at 08:25
  • There are tons of options. The constructor is meant for this purpose, so use it. – Patrick Hofman Apr 25 '18 at 08:27
  • "drop your idea of using collection initializers for something it isn't fit for." I don't see why this syntactic sugar can't be applied to AddRange() as well in the case we don't own the target object. – Larry May 05 '23 at 12:43
0

Is there a way to create the object with the List properly initialized in the first place (without adding a constructor, of course)?

No

It's not. That's what a constructor does. If you don't want to do it in the constructor, there is no way to do it.

Community
  • 1
  • 1
nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • 1
    Sure, but since it is possible to initialize like `TheList = {"A", "B"}`, I thought there might be a way to do this without touching the constructor. Thanks for your answer anyway! – MaSiMan Apr 25 '18 at 08:19
  • 1
    I guess it depends what you mean by "initialize". A constructor is initializing, after that it's just additional code that can be called... or not, that's up to the user of the class. You can have multiple constructors, if you don't want the original signature to change. – nvoigt Apr 25 '18 at 08:23
0

it is not possible to initialize a read only property from outside of the class itself.

collection initializer is just a simplified syntax version and it does not mean using this syntax you have the same access as if you are in the class constructor

 thirdHolder.TheList = existingList; // this is the traditional way

Perhaps you can use factory class pattern like this

   internal class Program
{
    public static void Main(string[] args)
    {
        // Compiles, but is not what I need
        var firstHolder = new Holder()
        {
            TheList = { "A", "B" }
        };

        // Compiles, but initializes the list after object creation
        var existingList = new List<string>() { "Foo", "Bar" };
        var secondHolder = new Holder();
        secondHolder.TheList.AddRange(existingList);

        // Does not compile
        //var thirdHolder = new Holder()
        //{
        //    TheList =  existingList 
        //};

        //thirdHolder.TheList = existingList; // this is the traditional way
        var thirdHolder = Holder.HolderFactory(existingList);
    }
}


internal class Holder
{
    public Holder()
    {
        TheList = new List<string>();
    }

    public static Holder HolderFactory(List<string> theList)
    {
        return new Holder(theList);
    }
    private Holder(List<string> theList)
    {
        this.TheList = theList;
    }
    public List<string> TheList { get; }
}
Jend DimShu
  • 87
  • 1
  • 5
  • I thought the constructor was the factory method for classes? – Patrick Hofman Apr 25 '18 at 08:27
  • it is different, factory method and constructor has the same purpose. it used to initialize the class. for simple initialization scenario it's fine to use it on constructor but for complex initialization you better use a factory method. see this link https://stackoverflow.com/questions/1519358/when-to-use-factory-method-pattern?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa – Jend DimShu Apr 25 '18 at 08:36