CA1819: свойства не должны возвращать массивы. Какая правильная альтернатива?

Я раньше встречался с этим правилом FxCop и не был доволен тем, как разрешать нарушения (thread1, thread2). У меня теперь есть другой случай, когда мне нужно исправить нарушения CA1819.

В частности, у меня есть библиотека алгоритмов, которая выполняет некоторые аналитические вычисления на кривой (x, y) с открытым "входным объектом" следующим образом:

public class InputObject
{
        public double[] X { get; set; }
        public double[] Y { get; set; }
        // + lots of other things well
}

Свойства объекта X и Y используются в сотнях мест в библиотеке, обычно с использованием индексов. Объект ввода никогда не изменяется с помощью алгоритмов, но на самом деле не имеет значения, если это так. Кроме того, .Length вызывается довольно часто. Это математическая библиотека, а double[] - вид стандартного типа данных. В любом случае для фиксации CA1819 потребуется довольно много работы.

Я думал об использовании List<double>, поскольку списки поддерживают индексирование и очень похожи на массивы, но я не уверен, может ли это замедлить алгоритмы, или FxCop будет доволен этими списками.

Каков наилучший вариант для замены этих свойств double[]?

Ответ 1

Если он читается только внешнему потребителю, а потребитель не хочет получать к нему доступ по индексу, то лучше всего иметь общедоступное свойство только для чтения типа IEnumerable<> с помощью методов доступа для добавления и удаления, таким образом вы не будете должны выставлять ваш массив кому-то, с кем можно связаться.

Если вам нужно получить доступ к индексаторам, тогда выведите его как свойство только для чтения типа IList<> и, вероятно, верните экземпляр ReadOnly с помощью методов для добавления и удаления.

Таким образом, вы сохраняете инкапсуляцию внутреннего списка и разрешаете пользователю получать доступ к нему только для чтения.

Ответ 2

Как показывает ваша ссылка:

Чтобы исправить нарушение этого правила, сделайте свойство методом или измените свойство, чтобы вернуть коллекцию.

Использование коллекции, такой как List, не должно оказывать существенного влияния на производительность.

Ответ 3

Большая проблема здесь не в том, что ваша библиотека делает со значениями (что потенциальная проблема, хотя и гораздо более управляемая), а скорее то, что вызывающие могут делать со значениями. Если вам нужно рассматривать их как неизменяемые, вам необходимо убедиться, что потребитель библиотеки не может изменить содержимое после их первоначального назначения. Легко исправить здесь было бы создание интерфейса, который предоставляет все члены массива, которые использует ваша библиотека, а затем создать неизменяемый класс-оболочку для массива, который реализует этот интерфейс для использования в вашем классе InputObject. например:.

public interface IArray<T>
{
    int Length { get; }

    T this[int index] { get; }
}

internal sealed class ImmutableArray<T> : IArray<T>
    where T : struct
{
    private readonly T[] _wrappedArray;

    internal ImmutableArray(IEnumerable<T> data)
    {
        this._wrappedArray = data.ToArray();
    }

    public int Length
    {
        get { return this._wrappedArray.Length; }
    }

    public T this[int index]
    {
        get { return this._wrappedArray[index]; }
    }
}

public class InputObject
{
    private readonly IArray<double> _x;
    private readonly IArray<double> _y;

    public InputObject(double[] x, double[] y)
    {
        this._x = new ImmutableArray<double>(x);
        this._y = new ImmutableArray<double>(y);
    }

    public IArray<double> X
    {
        get { return this._x; }
    }

    public IArray<double> Y
    {
        get { return this._y; }
    }

    //...
}

Элементы в вашем неизменном содержимом массива все равно будут изменчивыми, если T изменен, но по крайней мере вы безопасны для двойного типа.

Ответ 4

Когда-то FxCop с моей точки зрения exagerates.

Все зависит от того, что вам нужно делать, если вы пишете сложную систему, в которой требуется безопасность и очень чистый код, вы должны вернуть версию только для чтения этого массива. То есть, бросьте массив как IEnumerable, как предполагает devdigital или используйте хорошую идею ImmutableArray из Mohamed Abed, которую я предпочитаю.

Если вы пишете программное обеспечение, требующее высокой производительности... нет ничего лучше, чем массив для исполнения на С#. Массивы могут быть намного более эффективными для повторения и чтения.

Если выступления действительно важны, я предлагаю вам игнорировать это предупреждение. Является законным, также если не слишком много, чтобы вернуть массив readonly.

for (int i = 0; i < array.Length; ++i) { k = array[i] + 1; }

Это очень быстро для больших массивов в С#: он избегает проверки границ массива. Он будет выполнять очень много, как это сделал бы скомпилированный код C.

Мне всегда нравился тип "readonly array" в С#:), но нет надежды увидеть его.