之前我遇到过这个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>
,因为Lists支持索引并且与数组非常相似,但我不确定这是否会降低算法速度或者FxCop是否对这些列表感到满意。
替换这些double[]
属性的最佳选择是什么?
答案 0 :(得分:8)
如果它只读给外部消费者并且消费者不希望通过索引访问它,那么最好是拥有类型为IEnumerable<>
的公共只读属性,并添加和删除方法访问器,这样你就可以了不必将你的数组暴露给有人搞乱。
如果需要访问索引器,则将其公开为类型为IList<>
的只读属性,并可能返回一个ReadOnly实例,并添加和删除方法。
这样您就可以保留内部列表的封装,并允许消费者以只读方式访问它
答案 1 :(得分:1)
正如您的链接所示:
要修复违反此规则的行为,请将该属性设为方法或 更改属性以返回集合。
使用诸如List
之类的集合不应对性能产生重大影响。
答案 2 :(得分:0)
这里的一个大问题并不是你的库对值的影响(这是一个潜在的问题,尽管是一个更易于管理的问题),而是调用者可能会对这些值做些什么。如果需要将它们视为不可变,则需要确保库使用者在原始分配后不能更改内容。这里的简单修复是创建一个接口,公开您的库使用的所有数组成员,然后为实现此接口的数组创建一个不可变的包装类,以便在InputObject
类中使用。 e.g :
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类型是安全的。
答案 3 :(得分:0)
从某些时候起,FxCop会从我的角度来看。
这一切都取决于你要做什么,如果你正在编写一个需要安全性和非常干净的代码的复杂系统,你应该返回该数组的只读版本。 也就是说,将数组转换为IEnumerable,如建议devdigital或使用Mohamed Abed的好主意ImmutableArray,我更喜欢。
如果您正在编写需要高性能的软件......没有什么比C#中的表演更好的了。 对于迭代和阅读,数组可以更高效。
如果表演真的很重要,我建议你忽略那个警告。 如果不是太干净,仍然是合法的,返回一个只读数组。
for (int i = 0; i < array.Length; ++i) { k = array[i] + 1; }
对于C#中的大数组来说,这是非常快的:它避免了数组边界检查。 它会像C编译代码那样执行。
我总是希望在C#:)中使用“readonly array”类型,但是没有希望看到它。
答案 4 :(得分:0)
将数组 [] 更改为 IEnumerable:
public class InputObject
{
public IEnumerable<double> X { get; set; }
public IEnumerable<double> Y { get; set; }
// + lots of other things well
}