CA1819: свойства не должны возвращать массивы. Какова правильная альтернатива? - PullRequest
13 голосов
/ 19 октября 2011

Я сталкивался с этим правилом 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 ]

8 голосов
/ 19 октября 2011

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

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

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

1 голос
/ 19 октября 2011

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

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

0 голосов
/ 19 октября 2011

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

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

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

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

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

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

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

0 голосов
/ 19 октября 2011

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

...