Home > other >  Is it possible to initialise a readonly collection property using a collection rather than individua
Is it possible to initialise a readonly collection property using a collection rather than individua

Time:01-24

In C# you can initialise a collection property as such:

public class MyClass
{
    public string Name { get; set ;}
}

public class MyContainerClass
{
    public ICollection<MyClass> Collection { get; set; }
}

var myObject = new MyContainerClass
               {
                   Collection = new List<MyClass>
                                {
                                    new()
                                    {
                                       Name = "1",
                                    },
                                    new()
                                    {
                                       Name = "2",
                                    },
                                },
                            }

For a class with a pre-instantiated, readonly collection you can do something similar:

public class MyReadonlyContainerClass
{
    public ICollection<MyClass> Collection { get; } = new List<MyClass>();
}

var myObject = new MyReadonlyContainerClass
               {
                   Collection = {
                                    new()
                                    {
                                       Name = "1",
                                    },
                                    new()
                                    {
                                       Name = "2",
                                    },
                                },
                            };

What I would like to know if is there is any way to use initialisation to add the members of an existing collection to a readonly collection class. For example, it might look like:

var myCollection = Enumerable.Range(1,10).Select(i => new MyClass{ Name = i.ToString() });

var myObject = new MyReadonlyContainerClass{ Collection = { myCollection } };

The above does not compile as the Add method of List<T> takes a single T instance rather than an IEnumerable<T> but I was wondering if there is a syntactically correct way to do this. Something like the spread operator in javascript maybe?

Update for context

I think from the comments below I should have been clearer about why I would like a solution to this. I am using EF Core Power Tools to generate C# classes from an existing DB schema. The EF 7 version has removed the setter from the autogenerated navigation properties (explained here). We have a test suite which will be time consuming to refactor that has instances of initialising navigation properties in the way I explained above. If a refactor is necessary then so be it, I was just looking for a language feature I may have overlooked.

CodePudding user response:

If the concrete type of the property was List<T> rather than ICollection<T> then you could define an extension method called Add that takes the collection of items and calls List's AddRange method instead:

public static class Program
{
    static void Main(string[] args)
    {
        var myCollection = Enumerable.Range(1, 10).Select(i => new MyClass { Name = i.ToString() });

        var myObject = new MyReadonlyContainerClass { Collection = { myCollection } };
        Console.ReadLine();
    }
}

public class MyReadonlyContainerClass
{
    public List<MyClass> Collection { get; } = new List<MyClass>();
}


public class MyClass
{
    public string Name { get; set; }
}

public static class ConvolutedInit {
    public static void Add<T>(this List<T> collection, IEnumerable<T> items)
    {
        collection.AddRange(items);
    }
}

However, I put this in the realm of silly tricks to play with the compiler rather than something I'd recommend putting into production code. It's just confusing enough that it's more likely to trip up future readers rather than simplify things.

There's no built-in support for such a feature since AddRange isn't part of any common interface for collections.

CodePudding user response:

Thanks to the earlier answer from @Damien_The_Unbeliever I was able to work out a solution. I added an extension method to the ICollection interface.

public static class ICollectionExtensions
{
    public static void Add<T>(this ICollection<T> collection, IEnumerable<T> rangeToAdd)
    {
        foreach (var item in rangeToAdd)
        {
            collection.Add(item);
        }
    }
}

and the following code now compiles:

var myCollection = Enumerable.Range(1,10).Select(i => new MyClass{ Name = i.ToString() });

var myObject = new MyReadonlyContainerClass{ Collection = { myCollection } };

Relatively straightforward solution in the end, thanks for the help.

  • Related