正确公开List <T>?

Mic*_*tum 12 .net c#

我知道我不应该暴露一个List<T>属性,但我想知道这样做的正确方法是什么?例如,这样做:

public static class Class1
{
    private readonly static List<string> _list;

    public static IEnumerable<string> List
    {
        get
        {
            return _list;
            //return _list.AsEnumerable<string>(); behaves the same
        }
    }

    static Class1()
    {
        _list = new List<string>();
        _list.Add("One");
        _list.Add("Two");
        _list.Add("Three");
    }
}
Run Code Online (Sandbox Code Playgroud)

允许我的来电者简单地回到List<T>:

    private void button1_Click(object sender, EventArgs e)
    {
        var test = Class1.List as List<string>;
        test.Add("Four"); // This really modifies Class1._list, which is bad™
    }
Run Code Online (Sandbox Code Playgroud)

所以,如果我想要一个真正不可变的List<T>,我总是要创建一个新的列表?例如,这似乎有效(测试在转换后为null):

    public static IEnumerable<string> List
    {
        get
        {
            return new ReadOnlyCollection<string>(_list);
        }
    }
Run Code Online (Sandbox Code Playgroud)

但是我担心是否存在性能开销,因为每次有人试图访问它时我的列表都被克隆了?

Mar*_*ell 8

揭露List<T>作为财产实际上并不是万恶之源; 特别是如果它允许预期的使用,如foo.Items.Add(...).

您可以编写一个安全的替代品AsEnumerable():

public static IEnumerable<T> AsSafeEnumerable<T>(this IEnumerable<T> data) {
    foreach(T item in data) yield return item;
}
Run Code Online (Sandbox Code Playgroud)

但目前你最大的问题是线程安全.作为一个静态成员,你可能在这里遇到很大的问题,特别是如果它是像ASP.NET这样的东西.即使ReadOnlyCollection在现有列表中,也会受此影响:

        List<int> ints = new List<int> { 1, 2, 3 };
        var ro = ints.AsReadOnly();
        Console.WriteLine(ro.Count); // 3
        ints.Add(4);
        Console.WriteLine(ro.Count); // 4
Run Code Online (Sandbox Code Playgroud)

因此,简单地用包装纸AsReadOnly不是足以让你的对象是线程安全的; 它只是防止消费者添加数据(但是当你的其他线程添加数据时,他们仍然可以枚举它,除非你同步或复制).