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[]?

4 12

4 ответа:

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

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

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

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

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

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

Большая проблема здесь заключается не в том, что ваша библиотека делает со значениями (что является потенциальной проблемой, хотя и гораздо более управляемой), а скорее в том, что вызывающие могут делать со значениями. Если вам нужно рассматривать их как неизменяемые, то вы должны убедиться, что потребитель библиотеки не может изменить содержимое после их первоначального назначения. Проще всего было бы создать интерфейс, который предоставляет доступ ко всем членам массива, используемым библиотекой, а затем создать неизменяемый класс-оболочку для массив, реализующий этот интерфейс для использования в вашем классе 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 изменяемо, но, по крайней мере, вы в безопасности для типа double.

Иногда FxCop с моей точки зрения преувеличивает.

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

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

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

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

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

Я всегда хотел иметь тип" readonly array " В C#:), но нет никакой надежды увидеть его.