CA1819:属性不应该返回数组——正确的替代方法是什么?

本文关键字:方法 是什么 属性 不应该 返回 数组 CA1819 | 更新日期: 2023-09-27 18:11:46

我以前遇到过这个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[]属性的最佳选择是什么?

CA1819:属性不应该返回数组——正确的替代方法是什么?

如果它对外部消费者是只读的,并且消费者不想通过索引访问它,那么最好是有一个IEnumerable<>类型的公共只读属性,并带有可以添加和删除的方法访问器,这样你就不必把你的数组暴露给别人来弄乱。

如果你需要访问索引器,那么将它公开为IList<>类型的只读属性,并可能返回一个只读实例,并带有添加和删除的方法。

这种方式可以保持内部列表的封装,并允许消费者以只读方式访问它

有时候FxCop在我看来是夸大其词了。

这完全取决于你要做什么,如果你正在编写一个需要安全性和非常干净的代码的复杂系统,你应该返回该数组的只读版本。也就是说,将数组转换为IEnumerable,就像devdigital建议的那样,或者使用Mohamed Abed的ImmutableArray这个好主意,我更喜欢。

如果你正在编写需要高性能的软件…在c#中,没有什么比数组更能提高性能了。对于迭代和读取,数组的性能要高得多。

如果性能真的很重要,我建议你忽略这个警告。如果不是太干净的话,返回一个只读数组仍然是合法的。

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

对于c#中的大数组来说,这是非常快的:它避免了数组边界检查。它的性能与C编译后的代码非常相似。

我一直希望在c#中有一个"只读数组"类型:)但是没有希望看到它。

如你的链接所示:

要修复违反此规则的情况,要么使属性成为方法,要么更改属性以返回一个集合。

使用像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类型是安全的。

将array[]改为IEnumerable:

public class InputObject
{
    public IEnumerable<double> X { get; set; }
    public IEnumerable<double> Y { get; set; }
    // + lots of other things well
}