我尝试做这样的事情:
public const List<String> METRICS = new List<String>()
{
SourceFile.LOC,
SourceFile.MCCABE,
SourceFile.NOM,
SourceFile.NOA,
SourceFile.FANOUT,
SourceFile.FANIN,
SourceFile.NOPAR,
SourceFile.NDC,
SourceFile.CALLS
};
但不幸的是,这不起作用:
FileStorer.METRICS' is of type 'System.Collections.Generic.List<string>'. A const field of a reference type other than string can only be initialized with null.
我该如何解决这个问题?
答案 0 :(得分:163)
const
用于编译时常量。您可以只使其成为static readonly
,但这只适用于METRICS
变量本身(通常应该是.NET naming conventions的度量标准)。它不会使列表不可变 - 所以有人可以调用METRICS.Add("shouldn't be here");
您可能希望使用ReadOnlyCollection<T>
来包装它。例如:
public static readonly IList<String> Metrics = new ReadOnlyCollection<string>
(new List<String> {
SourceFile.LoC, SourceFile.McCabe, SourceFile.NoM,
SourceFile.NoA, SourceFile.FanOut, SourceFile.FanIn,
SourceFile.Par, SourceFile.Ndc, SourceFile.Calls });
ReadOnlyCollection<T>
只包装一个可能可变的集合,但由于之后没有任何其他内容可以访问List<T>
,您可以将整个集合视为不可变的。
(这里的大写主要是猜测 - 使用更全面的名称会让他们更清楚,IMO。)
无论您将其声明为IList<string>
,IEnumerable<string>
,ReadOnlyCollection<string>
还是别的东西都取决于您...如果您希望它只能被视为序列,那么{ {1}}可能是最合适的。如果订单很重要并且您希望人们能够通过索引访问它,IEnumerable<string>
可能是合适的。如果你想使不变性显而易见,将其声明为IList<T>
可能很方便 - 但不灵活。
答案 1 :(得分:21)
您需要使用static
readonly
列表。如果您希望列表不可变,那么您可能需要考虑使用ReadOnlyCollection<T>
而不是List<T>
。
private static readonly ReadOnlyCollection<string> _metrics =
new ReadOnlyCollection<string>(new[]
{
SourceFile.LOC,
SourceFile.MCCABE,
SourceFile.NOM,
SourceFile.NOA,
SourceFile.FANOUT,
SourceFile.FANIN,
SourceFile.NOPAR,
SourceFile.NDC,
SourceFile.CALLS
});
public static ReadOnlyCollection<string> Metrics
{
get { return _metrics; }
}
答案 2 :(得分:1)
.NET支持真正的不可变集合,可变集合的只读视图以及可变集合实现的只读接口。
一个这样的不可变集合是 ImmutableArray&lt;&gt; ,您可以在示例中将其创建为a.ToImmutableArray()。请务必查看MSDN列表中的其他选项,因为不同的不可变集合可能会为您提供更好的服务。如果您想稍微修改原始序列的副本,则ImmutableList&lt;&gt;例如,可能更快(创建和访问数组的成本更低)。注意a.Add(...);是有效的,但返回一个新的集合而不是更改一个。如果你有resharper,那么如果你忽略像Add这样的纯方法的返回值,那将警告你(并且可能有一个roslyn扩展来做类似我不知道的事情)。如果你要走这条路线 - 考虑跳过List&lt;&gt;完全直接进入不可变的集合。
可变集合的只读视图不太安全,但在旧版本的.NET上受支持。包装类型称为ReadOnlyCollection&lt;&gt;,在您的示例中,您可以将其构造为a.AsReadOnly()。此系列不保证不变性;只有保证你不能改变它。共享对基础List&lt;&gt;的引用的其他一些代码。仍然可以改变它。此外,ReadOnlyCollection还会产生一些额外的开销;因此,出于性能原因,您可能无法通过避免不可变集合获得很多奖励(TODO:基准此声明)。您甚至可以在公共API中安全地使用诸如此类的只读包装器 - 没有(非反射)方式获取基础列表。但是,由于它通常不会比不可变集合快,并且它也不完全安全,我建议避免使用ReadOnlyCollection&lt;&gt; - 我个人不再使用它了。
可变集合实现的只读接口甚至更安全,但速度更快。您可以简单地投射List&lt;&gt;作为IReadOnlyList&lt;&gt;,您可以在您的示例中将其作为IReadOnlyList lst = a。这是我对内部代码的偏好 - 您仍然可以获得静态类型安全性,您不会受到恶意代码或使用类型检查和不明智的强制转换的代码的保护(但根据我的经验,这些可以通过代码审查来避免)。我从未被这种选择所困扰,但它比上述两种选择更不安全。从好的方面来说,它不会产生任何分配,而且速度更快。如果你经常这样做,你可能想要定义一个扩展方法来为你做upcast(在C#中强制转换可能是不安全的,因为它们不仅可以进行安全的上传,而且可能会使向下转发失败,以及用户定义的转换 - 所以它是一个很好的在任何可能的地方避免显式演员的想法。
请注意,在所有情况下,只有序列本身是只读的。底层对象不受影响(例如,int或string是不可变的,但更复杂的对象可能是也可能不是。)
答案 3 :(得分:0)
您正在寻找一个简单的代码,例如:
List<string> tagList = new List<string>(new[]
{
"A"
,"B"
,"C"
,"D"
,"E"
});