Встроенный экземпляр списка констант
Я пытаюсь сделать что-то вроде этого:
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.
Как я могу решить эту проблему?
3 ответа:
const
для констант времени компиляции. Ты может просто сделать этоstatic readonly
, но это относится только кMETRICS
сама переменная (которая обычно должна быть метрикой вместо этого, по.NET соглашения об именах). Это не сделает список неизменный-чтобы кто-то мог позвонить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>
или что-то еще до вас... если вы ожидаете, что он должен рассматриваться только как последовательность, тоIEnumerable<string>
вероятно, было бы наиболее подходящим. Если порядок имеет значение, и вы хотите, чтобы люди были возможность доступа к нему по индексу,IList<T>
может быть уместным. Если вы хотите сделать неизменность очевидной, объявив ее какReadOnlyCollection<T>
может быть удобно - но жесткой.
вам нужно использовать
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; } }
.NET поддерживает действительно неизменяемые коллекции, представления только для чтения изменяемых коллекций и интерфейсы только для чтения, реализованные изменяемой коллекцией s.
одним из таких неизменяемых коллекций является ImmutableArray который вы можете создать как. ToImmutableArray () в вашем примере. Не забудьте взглянуть на другие параметры списков MSDN, потому что вы можете быть лучше обслуживаются другой неизменяемой коллекции. Если вы хотите сделать копии исходной последовательности с небольшие изменения, ImmutableList может быть быстрее, например (массив дешевле для создания и доступа, хотя). Обратите внимание, что.Добавить(...); действителен, но возвращает новую коллекцию, а не изменяет a. если у вас есть resharper, это предупредит вас, если вы игнорируете возвращаемое значение чистого метода, такого как Add (и может быть расширение roslyn, чтобы сделать что-то подобное, о чем я не знаю). Если вы идете по этому маршруту-рассмотрите возможность пропуска списка полностью и перехода прямо к неизменяемому коллекции.
представления только для чтения изменяемых коллекций немного менее безопасны, но поддерживаются в более старых версиях .NET. тип упаковки называется ReadOnlyCollection, который в вашем примере вы можете построить как.AsReadOnly(). Эта коллекция не гарантирует неизменность; она только гарантирует, что вы не можете ее изменить. Какой-то другой бит кода, который разделяет ссылку на базовый список, все еще может его изменить. Кроме того, ReadOnlyCollection также накладывает некоторые дополнительные накладные расходы; так что вы возможно, вы не выиграете много, избегая неизменяемых коллекций по причинам производительности (TODO: benchmark this claim). Вы можете использовать оболочку только для чтения, такую как это, даже в общедоступном API безопасно-нет (не отражающего) способа получения базового списка. Однако, поскольку это часто не быстрее, чем неизменяемые коллекции, и это также не совсем безопасно, я рекомендую избегать ReadOnlyCollection - я больше никогда не использую это лично.
интерфейсы только для чтения, реализованные с помощью mutable коллекции еще ниже по шкале безопасности, но быстро. Вы можете просто привести List как IReadOnlyList, что вы можете сделать в своем примере как IReadOnlyList lst = a. это мои предпочтения для внутреннего кода - Вы все еще получаете статическую безопасность типа, вы просто не защищены от вредоносного кода или кода, который использует проверки типа и бросает неразумно (но это можно избежать с помощью обзоров кода в моем опыте). Я никогда не был укушен этим выбором, но это менее безопасно, чем два вышеупомянутых варианта. На с другой стороны, он не несет никаких ассигнований и быстрее. Если вы часто делаете это, вы можете определить метод расширения, чтобы сделать приведения на вас (может быть небезопасным в C#, поскольку они не только безопасны приводит, но возможно не схождение, и определенные пользователем преобразования - так что это хорошая идея, чтобы избежать явного приведения везде, где только можно).
обратите внимание, что во всех случаях, только сама последовательность только для чтения. Базовые объекты не затрагиваются (например, int или string неизменяемы, но больше сложные объекты могут быть или не быть).