在对象初始化器中从现有集合初始化只读集合

5
我有一个只读集合属性的类。我想用现有集合中的值初始化该集合。
我知道可以使用集合初始化程序来初始化集合using a collection initializer。我也可以创建对象,然后在集合上使用AddRange添加现有集合的项。但这将创建一个带有空列表的对象,并在之后添加现有项。
是否有一种方法可以在第一次正确初始化List时创建对象(当然不需要添加构造函数)?
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; }
    }
}

这是一个只读属性。你为什么期望在不使用构造函数的情况下从外部初始化它呢?这正是构造函数存在的原因。 - Tim Schmelter
3个回答

2
不行。您无法从集合初始化器中分配此只读属性。毕竟它是只读的。 TheList = { "A", "B" } 可以工作,因为它在 TheList 上调用了 Add(每次添加一个项目),而不是创建并分配一个新实例,这是不允许的。 TheList = { existingList } 不起作用,因为存在类型问题(TheList = { existingList[0] } 起作用)。
您最好的选择是创建一个构造函数参数,并放弃使用集合初始化器的想法,因为它不适合这种情况。

感谢您的回答和解释,如果Add在内部调用,那么不能将其与现有列表一起使用是有道理的。 - MaSiMan
好的,对于 TheList = { existingList[0] } 是有效的。但那是现有列表中的一个项目。 - Patrick Hofman
你可以向类中添加一个方法,如果列表为空,则允许其初始化(但这仍然需要属性的私有setter)。 - H.J. Meijer
有很多选项。构造函数就是为此而设计的,请使用它。 - Patrick Hofman
放弃使用集合初始化器来做它不适合的事情。我不明白为什么这种语法糖不能同样应用于AddRange(),特别是在我们不拥有目标对象的情况下。 - Larry

0

无法从类外部初始化只读属性。

集合初始化程序只是一个简化的语法版本,这并不意味着使用此语法您具有与在类构造函数中相同的访问权限。

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

也许你可以像这样使用工厂类模式
   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; }
}

我曾认为构造函数是类的工厂方法? - Patrick Hofman
它们不同,工厂方法和构造函数有相同的目的。它们用于初始化类。对于简单的初始化场景,使用构造函数是可以的,但对于复杂的初始化,最好使用工厂方法。请参见此链接https://dev59.com/AnI_5IYBdhLWcg3wHfSr?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa - Jend DimShu

0

有没有一种方法可以在一开始就正确地初始化带有List的对象(当然不需要添加构造函数)?

没有

不行。那就是构造函数的功能。如果你不想在构造函数中这么做,那就没有其他方法可供选择了。


1
当然可以,但是既然可以像TheList = {"A", "B"}这样初始化,我认为可能有一种方法可以在不触及构造函数的情况下完成这个操作。无论如何,感谢您的回答! - MaSiMan
1
我猜这取决于你对“初始化”的理解。构造函数是初始化的,之后它只是可以被调用或不被调用的其他代码...这取决于类的用户。如果你不想改变原始签名,你可以有多个构造函数。 - nvoigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接