C#中的const列表

Ofe*_*gen 32 c# oop list

我想在C#中创建一个列表,在创建后我将无法添加或删除它中的项目.例如,我将创建列表;

List<int> lst = a;
Run Code Online (Sandbox Code Playgroud)

(a是现有列表),但在我无法编写代码之后(它会将其标记为错误):

lst.Add(2);
Run Code Online (Sandbox Code Playgroud)

Eam*_*nne 45

.NET支持真正不可变的集合,可变集合的只读视图和可变集合实现的只读接口.


一个这样的不可变集合ImmutableArray<>它可以作为建立a.ToImmutableArray()在你的榜样.请务必查看MSDN列表中的其他选项,因为不同的不可变集合可能会为您提供更好的服务.如果您想稍微修改原始序列的副本,ImmutableList<>可能会更快(例如,创建和访问阵列的成本更低).请注意,该选项a.Add(...);有效,但返回新集合而不是更改a.如果你有resharper,那么如果你忽略纯方法的返回值就会警告你Add(并且可能有一个roslyn扩展来做类似我不知道的事情).如果你要走这条路线 - 考虑List<>完全跳过并直接进入不可变的集合.

可变集合的只读视图不太安全,但在旧版本的.NET上受支持.调用包装类型ReadOnlyCollection<>,在您的示例中,您可以将其构造为a.AsReadOnly().此集合并不能保证不变性; 只有保证不能改变它.共享对底层的引用的其他一些代码List<>仍然可以改变它.此外,ReadOnlyCollection还会产生一些额外的开销; 因此,出于性能原因,您可能无法通过避免不可变集合获得太多收益(TODO:基准此声明).您可以即使在安全的公共API使用的只读包装像这样-有获取基础列表中没有(非反射)的方式.然而,由于它比一成不变的藏品往往没有更快,而且它也没有完全安全的,我建议避免 ReadOnlyCollection<> -我从来没有再使用此,个人.

可变集合实现的只读接口甚至更安全,但速度更快.您可以简单地转换List<>IReadOnlyList<>,您可以在您的示例中执行IReadOnlyList<int> lst = a.这是我对内部代码的偏好- 您仍然可以获得静态类型安全性,您不会受到恶意代码或使用类型检查和不明智的强制转换的代码的保护(但根据我的经验,这些可以通过代码审查来避免).我从来没有被这样的选择咬伤,但它比上述两个选项不太安全.从好的方面来说,它不会产生任何分配,而且速度更快.如果你经常这样做,你可能要定义的扩展方法做向上转型为你(铸件可以在C#不安全,因为他们不仅做到安全upcasts,但可能不能向下转型,以及用户定义的转换-所以这是一个很好的在任何地方都可以避免显式演员的想法).

请注意,在所有情况下,只有序列本身是只读的.底层对象不受影响(例如,一个intstring不可变,但更复杂的对象可能是也可能不是).


TL; DR:

  • 安全起见:用于a.ToImmutableArray()在.中创建不可变副本ImmutableArray<int>.
  • 对于性能:使用IReadOnlyList<int>以帮助防止以最小的性能开销内部代码偶然突变.请注意,有人可以将其强制转换为List<>(不要这样做),使其对公共API不那么"安全".
  • 避免 a.AsReadOnly()创建一个,ReadOnlyCollection<int>除非你正在处理一个不支持新的替代品的遗留代码库,或者你真的知道你在做什么并有特殊需求(例如,确实想要在其他地方改变列表并拥有一个只读视图).

  • 这是我第一次看到[`ReadOnlyCollection <T>`](https://msdn.microsoft.com/en-us/library/ms132474.aspx)被描述为"非常慢".有什么问题?它是额外堆对象的开销+对IList <T>`方法的转发方法调用吗?我们在谈论什么样的速度差异?你有基准吗? (7认同)
  • @JeanHominal我打算编写一个不强调性能的答案,因为我认为它有点误导,特别是没有更深入地讨论在这些情况下代码有什么作用和什么不使代码变慢.AFAIK,它并不是具体的ReadOnlyCollection,虽然这就是文本建议的内容...... (2认同)

Yuv*_*kov 10

你可以使用ImmutableList<T>/ ImmutableArray<T>来自System.Collections.ImmutableNuGet:

var immutable = ImmutableList<int>.Create(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

或者使用ToImmutableList扩展方法:

var immutable = mutableList.ToImmutableList();
Run Code Online (Sandbox Code Playgroud)

Add调用In-case ,*返回一个新的副本*并且不修改原始列表.但这不会导致编译时错误.


Pat*_*ood 8

你需要一个ReadonlyCollection.您可以通过调用从列表中创建一个List.AsReadOnly()

参考:https://msdn.microsoft.com/en-us/library/ms132474.aspx

  • 是的,这是真的,除了在提问者的情况下,他希望添加/删除/ etc操作无效.不可变列表将允许它并且每次都返回一个新的不可变列表.您是否可以修改列表中的项目不是规定的要求. (2认同)
  • @DavidArno我当然不推荐```ReadonlyCollection```,我尽可能地避免它.如果你看到我的答案,那么问题是,IMO最好通过`````````````````````````````````````````````````````````````````````````` 我的意思是偶然的误用. (2认同)

小智 6

为什么不使用IEnumerable?

IEnumerable<string> myList = new List<string> { "value1", "value2" };
Run Code Online (Sandbox Code Playgroud)

  • 该死的,是的。只是不要把简单的事情复杂化。 (2认同)

Alu*_*dad 5

我建议使用一个System.Collections.Immutable.ImmutableList<T>实例,但由类型的变量或属性引用System.Collections.Generic.IReadOnlyList<T>.如果你只是使用一个裸的不可变列表,你不会因为你的需要而添加错误.

System.Collections.Generic.IReadOnlyList<int> list = a.ToImmutableList();
Run Code Online (Sandbox Code Playgroud)


eln*_*gno 5

readonly作为已发布答案的替代方案,您可以将常规包装List<T>到将其公开为 的对象中IReadOnlyList

class ROList<T>
{
    public ROList(IEnumerable<T> argEnumerable)
    {
        m_list = new List<T>(argEnumerable);
    }

    private readonly List<T> m_list;
    public IReadOnlyList<T> List { get { return m_list; } }
}

void Main()
{
    var list = new  List<int> {1, 2, 3};
    var rolist = new ROList<int>(list);

    foreach(var i in rolist.List)
        Console.WriteLine(i);

    //rolist.List.Add(4); // Uncomment this and it won't compile: Add() is not allowed
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*son 5

这里最好的选择是使用IReadOnlyList<int>.

与之IReadOnlyList<int>相比,使用的优点List.AsReadOnly()ReadOnlyCollection<T>可以将a分配给a IList<T>,然后可以通过可写索引器访问.

示例澄清:

var original = new List<int> { 1, 2, 3 };
IReadOnlyList<int> readOnlyList = original;

Console.WriteLine(readOnlyList[0]); // Compiles.

readOnlyList[0] = 0; // Does not compile.

var readOnlyCollection = original.AsReadOnly();

readOnlyCollection[0] = 1; // Does not compile.

IList<int> collection = readOnlyCollection; // Compiles.

collection[0] = 1; // Compiles, but throws runtime exception. 
Run Code Online (Sandbox Code Playgroud)

使用a IReadOnlyList<int>避免了意外地将只读列表传递给接受a IList<>然后尝试更改元素的方法的可能性- 这将导致运行时异常.


Dmi*_*nko 5

它可能是IReadOnlyList<int>,例如

  IReadOnlyList<int> lst = a;
Run Code Online (Sandbox Code Playgroud)

所以初始列表 ( a) 是可变的lst不是。我们经常用于公共财产私人财产,例如IReadOnlyList<T>IList<T>

  public class MyClass {
    // class itself can modify m_MyList 
    private IList<int> m_MyList = new List{1, 2, 3};

    ...
    // ... while code outside can't  
    public IReadOnlyList<int> MyList {
      get {
        return m_MyList; 
      }
    }  
  }
Run Code Online (Sandbox Code Playgroud)