简体   繁体   English

C# 中的常量列表

[英]A const list in C#

I would like to create a list in C# that after its creation I won't be able to add or remove items from it.我想在 C# 中创建一个列表,在创建后我将无法添加或删除项目。 For example, I will create the list;例如,我将创建列表;

List<int> lst = a;

(a is an existing list), but after I won't be able to write the code (it will mark it as an error): (a 是现有列表),但在我无法编写代码之后(它会将其标记为错误):

lst.Add(2);

.NET supports truly immutable collections, read-only views of mutable collections, and read-only interfaces implemented by mutable collections. .NET 支持真正的不可变集合、可变集合的只读视图以及由可变集合实现的只读接口。


One such immutable collection is ImmutableArray<> which you can create as a.ToImmutableArray() in your example.其中一个不可变集合ImmutableArray<> ,您可以在示例a.ToImmutableArray()其创建为a.ToImmutableArray() Make sure to take a look at the other options MSDN lists because you may be better served by a different immutable collection.请务必查看 MSDN 列出的其他选项,因为不同的不可变集合可能会更好地为您服务。 If you want to make copies of the original sequence with slight modifications, ImmutableList<> might be faster, for instance (the array is cheaper to create and access, though).例如,如果您想复制原始序列并稍作修改, ImmutableList<>可能会更快(尽管创建和访问数组的成本更低)。 Note that a.Add(...);注意a.Add(...); is valid, but returns a new collection rather than changing a .是有效的,但返回一个新的集合而不是更改a . If you have resharper, that will warn you if you ignore the return value of a pure method like Add (and there may be a roslyn extension to do something similar I'm unaware of).如果你有 resharper,如果你忽略像Add这样的纯方法的返回值,它会警告你(并且可能有一个 roslyn 扩展来做类似的事情,我不知道)。 If you're going this route - consider skipping List<> entirely and going straight to immutable collections.如果你要走这条路 -考虑完全跳过List<>并直接进入不可变集合。

Read-only views of mutable collections are a little less safe but supported on older versions of .NET.可变集合的只读视图不太安全,但在旧版本的 .NET 上受支持。 The wrapping type is called ReadOnlyCollection<> , which in your example you might construct as a.AsReadOnly() .包装类型称为ReadOnlyCollection<> ,在您的示例中,您可以将其构造为a.AsReadOnly() This collection does not guarantee immutability;此集合保证不变性; it only guarrantees you can't change it.它只保证不能改变它。 Some other bit of code that shares a reference to the underlying List<> can still change it.共享对底层List<>的引用的其他一些代码仍然可以更改它。 Also, ReadOnlyCollection also imposes some additional overhead;此外,ReadOnlyCollection 还带来了一些额外的开销; so you may not be winning much by avoiding immutable collections for performance reasons (TODO: benchmark this claim).因此,出于性能原因避免使用不可变集合,您可能不会赢得太多收益(TODO:对这个声明进行基准测试)。 You can use a read-only wrapper such as this even in a public API safely - there's no (non-reflection) way of getting the underlying list.即使在公共 API 中,您也可以安全地使用这样的只读包装器 - 没有(非反射)获取底层列表的方法。 However, since it's often no faster than immutable collections, and it's also not entirely safe, I recommend to avoid ReadOnlyCollection<> - I never use this anymore, personally.然而,由于它往往不会高于一成不变的集合,它也并不是完全安全的,我建议避免ReadOnlyCollection<> -我从来没有再使用此,个人。

Read-only interfaces implemented by mutable collections are even further down the scale of safety, but fast.由可变集合实现的只读接口进一步降低了安全性,但速度很快。 You can simply cast List<> as IReadOnlyList<> , which you might do in your example as IReadOnlyList<int> lst = a .您可以简单地将List<>IReadOnlyList<> ,您可以在示例中将其转换为IReadOnlyList<int> lst = a This is my preferences for internal code - you still get static type safety, you're simply not protected from malicious code or code that uses type-checks and casts unwisely (but those are avoidable via code-reviews in my experience).这是我对内部代码的偏好 - 您仍然可以获得静态类型安全,您根本无法免受恶意代码或使用类型检查和不明智强制转换的代码的保护(但根据我的经验,这些可以通过代码审查来避免)。 I've never been bitten by this choice, but it is less safe than the above two options.我从来没有被这样的选择咬伤,但上述两个选项不太安全。 On the upside, it incurs no allocations and is faster.从好的方面来说,它不需要分配并且速度更快。 If you commonly do this, you may want to define an extension method to do the upcast for you (casts can be unsafe in C# because they not only do safe upcasts, but possibly failing downcasts, and user-defined conversions - so it's a good idea to avoid explicit casts wherever you can).如果你经常这样做,你可能想定义一个扩展方法来为你做向上转换(在 C# 中转换可能是不安全的,因为它们不仅安全向上转换,而且可能会失败向下转换和用户定义的转换 - 所以这是一个很好的尽可能避免显式强制转换的想法)。

Note that in all cases, only the sequence itself is read-only.请注意,在所有情况下,只有序列本身是只读的。 Underlying objects aren't affected (eg an int or string are immutable, but more complicated objects may or may not be).底层对象不受影响(例如intstring是不可变的,但更复杂的对象可能会也可能不会)。


TL;DR:特尔;博士:

  • For safety : Use a.ToImmutableArray() to create an immutable copy in an ImmutableArray<int> .为了安全:使用a.ToImmutableArray()ImmutableArray<int>创建一个不可变的副本。
  • For performance : Use IReadOnlyList<int> to help prevent accidental mutation in internal code with minimal performance overhead.对于性能:使用IReadOnlyList<int>以最小的性能开销帮助防止内部代码中的意外更改。 Be aware that somebody can cast it back to List<> (don't do that), making this less "safe" for a public api.请注意,有人可以将其转换回List<> (不要那样做),这对于公共 api 来说不那么“安全”。
  • Avoid a.AsReadOnly() which creates a ReadOnlyCollection<int> unless you're working on a legacy code base that doesn't support the newer alternatives, or if you really know what you're doing and have special needs (eg really do want to mutate the list elsewhere and have a read-only view).避免a.AsReadOnly()创建ReadOnlyCollection<int> a.AsReadOnly()除非您正在处理不支持较新替代方案的遗留代码库,或者如果您真的知道自己在做什么并且有特殊需求(例如真的想在其他地方改变列表并拥有只读视图)。

You can use ImmutableList<T> / ImmutableArray<T> from System.Collections.Immutable NuGet :您可以使用来自System.Collections.Immutable NuGet 的ImmutableList<T> / ImmutableArray<T>

var immutable = ImmutableList<int>.Create(1, 2, 3);

Or using the ToImmutableList extension method:或者使用ToImmutableList扩展方法:

var immutable = mutableList.ToImmutableList();

In-case Add is invoked, *a new copy * is returned and doesn't modify the original list.如果调用Add ,则返回 *a new copy * 并且不会修改原始列表。 This won't cause a compile time error though.但这不会导致编译时错误。

You need a ReadonlyCollection .你需要一个ReadonlyCollection You can create one from a list by calling List.AsReadOnly()您可以通过调用List.AsReadOnly()从列表中创建一个

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

为什么不直接使用 IEnumerable?

IEnumerable<string> myList = new List<string> { "value1", "value2" };

I recommend using a System.Collections.Immutable.ImmutableList<T> instance but referenced by a variable or property of type System.Collections.Generic.IReadOnlyList<T> .我建议使用System.Collections.Immutable.ImmutableList<T>实例,但由System.Collections.Generic.IReadOnlyList<T>类型的变量或属性引用。 If you just use a naked immutable list, you won't get errors for adding to it, as you desire.如果您只使用裸不可变列表,则不会根据需要添加到它的错误。

System.Collections.Generic.IReadOnlyList<int> list = a.ToImmutableList();

Your best bet here is to use an IReadOnlyList<int> .最好的办法是使用IReadOnlyList<int>

The advantage of using IReadOnlyList<int> compared to List.AsReadOnly() is that a ReadOnlyCollection<T> can be assigned to an IList<T> , which can then be accessed via a writable indexer.List.AsReadOnly()相比,使用IReadOnlyList<int>的优点是可以将ReadOnlyCollection<T>分配给IList<T> ,然后可以通过可写索引器访问它。

Example to clarify:举例说明:

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. 

Using an IReadOnlyList<int> avoids the possibility of accidentally passing the read-only list to a method which accepts an IList<> and which then tries to change an element - which would result in a runtime exception.使用IReadOnlyList<int>避免了将只读列表意外传递给接受IList<>的方法的可能性,该方法然后尝试更改元素 - 这将导致运行时异常。

As an alternative to the already posted answers, you can wrap a readonly regular List<T> into an object that exposes it as IReadOnlyList .作为已经发布的答案的替代方案,您可以将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
}

It could be IReadOnlyList<int> , eg它可能是IReadOnlyList<int> ,例如

  IReadOnlyList<int> lst = a;

So the initial list ( a ) is mutable while lst is not .所以初始列表 ( a ) 是可变的,lst不是 Often we use IReadOnlyList<T> for public properties and IList<T> for private ones, eg通常我们将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; 
      }
    }  
  }

为什么不只是:

readonly IEnumerable<int> lst = new List<int>() { a }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM