【问题标题】:CA1819: Properties shouldn't return arrays - What is the right alternative?CA1819:属性不应返回数组 - 什么是正确的选择?
【发布时间】:2011-12-10 21:11:07
【问题描述】:

我之前遇到过这个 FxCop 规则,对如何解决违规问题并不满意(thread1thread2)。我现在有另一个案例,我需要更正违反 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[] 属性的最佳选择是什么?

【问题讨论】:

  • @skk 这是问题链接到的“thread1”...
  • (注意必须对尖括号进行编码格式才能显示)

标签: c# .net fxcop


【解决方案1】:

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

如果您需要访问索引器,请将其公开为 IList<> 类型的只读属性,并可能返回一个 ReadOnly 实例,其中包含添加和删除方法。

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

【讨论】:

  • 感谢您的输入,这就是我所做的.. 特别是因为还有另一个 FxCop 规则 (CA2227, msdn.microsoft.com/de-de/library/ms182327.aspx) ,这迫使我这样做。 ;)
  • 就个人而言,如果我想向调用者暗示某个属性可能会被延迟评估,我只会使用 IEnumerable。在 OP 的示例中可能不是这种情况,所以我更喜欢 IList,如果列表应该是只读的,则由 ReadOnlyCollection 字段支持。
  • 正是@Joe,我的意思正是:)
【解决方案2】:

在我看来,FxCop 有时会夸大其词。

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

如果您正在编写需要高性能的软件...在 C# 中,没有什么比数组更好的了。 数组在迭代和读取方面的性能要好得多。

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

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

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

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

【讨论】:

  • “我一直希望 C# 中有一个“只读数组”类型”——ReadOnlyCollection 在功能上等同于只读数组。
  • 绝对正确,但在性能上没有那么多,数组在 JIT 中有特殊的“shortcusts”。
  • 许多年过去了,但我们终于有了它 - ReadOnlySpan - docs.microsoft.com/en-us/dotnet/api/…
【解决方案3】:

正如您的链接所示:

要修复违反此规则的行为,请将属性设为方法或 更改属性以返回集合。

使用诸如List 之类的集合应该不会对性能产生重大影响。

【讨论】:

  • FxCop 也不喜欢List&lt;T&gt; - 更喜欢IList&lt;T&gt;
  • 感谢 Joe,我们不使用 FxCop,但这是有道理的
  • 根据@Joe,我认为将属性的返回值更改为 IList 应该不会对要重新编译/更改的其他代码产生任何影响,并解决 FxCop 问题..
【解决方案4】:

这里最大的问题并不是你的库对值做了什么(这是一个潜在的问题,尽管它更易于管理),而是调用者可能对这些值做了什么。如果您需要将它们视为不可变的,那么您需要确保库使用者在原始分配后无法更改内容。此处的简单解决方法是创建一个接口,该接口公开您的库使用的所有数组成员,然后为实现此接口的数组创建一个不可变的包装类,以在您的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 类型是安全的。

【讨论】:

  • 创建自己的接口(IArray)而不是使用框架中已经存在的类似 IList 似乎有点过头了。
  • @Joe: IList 公开了修改列表的操作。可以添加、删除和换出项目。当然,可以为每个突变点抛出 NotSupportedException,但这并不是对待 API 消费者的一种特别礼貌的方式。说真的,添加一个界面有多麻烦?
  • 它也是一个属性 IsReadOnly,它允许调用者避免 NotSupportedException。在 .NET 中处理此问题的惯用方法是公开一个只读 IList,可能由 ReadOnlyCollection 支持。您当前的实现有许多弱点,包括未能实现 IEnumerable、ICollection 和 IList,这是数组类所期望的。你可以花时间来实现这些,但是当框架已经包含了你需要的一切时,你为什么要这样做呢?
  • @Joe:这对 API 消费者来说是一件可怕的事情。有时这是必要的,但这并不是其中一种情况。遗憾的是,BCL 不包含只读集合接口,但这并不意味着当我们想要提供不可变性时,我们必须使用它提供的可变接口。 (顺便说一句,我的示例不完整,因为它是一个示例。它旨在展示核心方法,而不是完整的实现。)
  • 所有设计都是权衡取舍,包括在决定使用具有布尔“功能”属性的单个接口还是多个接口时。在这种情况下,我认为 BCL 没有只读集合接口是绝对正确的,原因不适合评论。在某些情况下,这两种选择都可能产生负面影响,因此如果没有仔细评估所有优缺点,就说一个“糟糕”是错误的——我相信 BCL 设计者在设计他们的 API 时就是这样做的。
【解决方案5】:

将数组 [] 更改为 IEnumerable:

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

【讨论】:

    猜你喜欢
    • 2014-12-06
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 2016-11-05
    • 1970-01-01
    • 1970-01-01
    • 2013-12-04
    相关资源
    最近更新 更多