C# 对 List<> 或其他集合或 ToArray() 魔术的只读访问权限
本文关键字:魔术 权限 读访问 集合 List 其他 ToArray | 更新日期: 2023-09-27 18:18:38
>第一个问题是关于保护我的列表免受外部更改(删除/添加/清除等...(的方法
有我的方式:
class Foo
{
public int[] MyCollection
{
get{ return (_myCollection==null)?null:_myCollection.ToArray();
}
protected List<int> _myCollection;
}
好不好?或者有什么更好的想法,或者可能是模式?
第二:当我用秒表测试这个解决方案时,我感到非常惊讶。
List-enumeration 比 List.ToArray(( 枚举慢,具有强制转换时间:
List<int> myList = new List<int>();
for (int j = 0; j < 10000; j++)
{
myList.Add(j);
}
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000; i++)
{
//casting every iteration:
var ROC = myList.ToArray();
int count = 0;
foreach (var a in ROC)
{
count += a;
}
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
它向我显示 700 毫秒,并且
List<int> myList = new List<int>();
for (int j = 0; j < 10000; j++)
{
myList.Add(j);
}
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000; i++)
{
int count = 0;
//No casting at all
foreach (var a in myList)
{
count += a;
}
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
显示 843 毫秒...为什么会这样?
首先,如果您有两个问题,请发布两个问题。 我会回答你的第一个问题。
其次,对你思考如何保护你的列表免受突变有好处。很多人忘记了这一点,并将他们的内部状态暴露给呼叫者。
您可以做很多事情。
1(首先不要使用可变列表。使用不可变列表。不可变列表的 Add
方法返回不同的列表,而不是改变当前列表。BCL 中现在有新的不可变列表类。这些在空间和时间上都非常有效。
2(使用可变列表并每次都制作副本。 你已经在这样做了。问题当然是它很慢并且使用大量内存。
3(按照克劳迪奥的建议返回AsReadOnly
。 请注意,这只是在列表周围形成只读外观;如果列表更改,只读外观也会更改。"只读"的意思就是:用户无法写入它。这并不意味着它永远不会改变。此外,还有一个枚举问题,我将在 (4( 中解决:
4( 让 LINQ 完成工作。 return _myCollection.Select(x=>x);
并使属性的类型为 IEnumerable<int>
。 这有两个缺点。首先,调用方仅获取转发枚举。其次,假设调用方正在对属性执行foreach
,然后在循环中执行更改列表的操作。这将导致异常;枚举列表时无法更改列表。如果您需要支持该方案,那么 (1( 或 (2( 是您的最佳选择;在这些中,枚举将覆盖快照,而不是突变列表。
对于只读集合,您可以使用List<T>.AsReadOnly()
public IList<int> MyCollection
{
get{ return _myCollection==null ? null : _myCollection.AsReadOnly();
}
如有必要,为了更清楚地表明我们正在谈论只读集合,您可以像这样定义您的属性
public IReadOnlyList<int> MyCollection
对问题中第二个问题的回答
迭代 int 数组与迭代整数列表会在 IL 代码级别产生非常不同的输出。
遍历整数数组
IL_0015: ldloc.3
IL_0016: ldloc.s CS$7$0001
IL_0018: ldelem.i4
IL_0019: stloc.2
IL_001a: ldloc.1
IL_001b: ldloc.2
IL_001c: add
IL_001d: stloc.1
IL_001e: ldloc.s CS$7$0001
IL_0020: ldc.i4.1
IL_0021: add
IL_0022: stloc.s CS$7$0001
IL_0024: ldloc.s CS$7$0001
IL_0026: ldloc.3
IL_0027: ldlen
IL_0028: conv.i4
IL_0029: blt.s IL_0015
循环访问整数列表
IL_0010: ldloca.s CS$5$0000
IL_0012: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
IL_0017: stloc.1
IL_0018: ldloc.0
IL_0019: ldloc.1
IL_001a: add
IL_001b: stloc.0
IL_001c: ldloca.s CS$5$0000
IL_001e: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
IL_0023: brtrue.s IL_0010
这里的关键是,当使用列表时,CLR 每次迭代都会执行一个类型(get_Current()
对象类型int
(。这可能会导致性能问题。