rse*_*nna 9 .net c# memory performance c#-3.0
也许这是一个愚蠢的问题......但是创造一个常数IEnumerable<TSomeType>...... 最好的(表现和记忆的)方式是什么?
如果无法定义"最佳"方式,哪些是我的选择?您有什么看法,您认为有最合适的方法吗?
例如:
var enumerable = (IEnumerable<TSomeType>) new List<TSomeType> { Value1, Value2, Value3 };var enumerable = (IEnumerable<TSomeType>) new TSomeType[] { Value1, Value2, Value3 };请考虑内存和性能是一个问题 - 我们谈论的是一个非常有限的环境(安装了.NET的小型设备).
提前致谢.
好吧,既不List<T>是数组也不是数组都是不可变的,所以如果你真的在不可变性之后它们会被淘汰 - 调用者可以转换结果然后修改它.
你可以创建一个List<T>并将其包装在一个ReadOnlyCollection<T>.如果没有任何东西对原始列表有任何引用,那么它实际上是不可变的,除非反射.
如果你实际上并不关心不变性 - 即如果你相信所有的代码都不会弄乱它 - 那么数组几乎可以肯定是最高效的方法.有各种CLR级别的优化使它们的工作速度极快.但是,在这种情况下我不会强制转换IEnumerable<T>- 我只是将它暴露为数组.与编译器必须调用相比,这将使迭代更快GetEnumerator().
如果C#编译器foreach在数组上看到一个语句,它会生成调用直接转到索引器并使用该Length属性......然后CLR也能够删除边界检查,发现模式.
同样,如果你决定一起去List<T>,那就把它留下来List<T>- 这样你就可以List<T>.Enumerator直接使用- 这是一个结构 - 没有拳击.
编辑:Steve Megson提出了使用LINQ的观点.实际上,你可能做得更好,因为一旦你有了基础列表的枚举器,你可以安全地将它返回给调用者,至少对于我所知道的所有集合.所以你可以:
public class ProtectedEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> collection;
public ProtectedEnumerable(IEnumerable<T> collection)
{
this.collection = collection;
}
public IEnumerator<T> GetEnumerator()
{
return collection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Run Code Online (Sandbox Code Playgroud)
这意味着迭代时只有一点点命中 - 只需要一次委托调用GetEnumerator().将其与使用相比较,Enumerable.Select在每次调用时都需要额外的委托命中MoveNext()(以及无操作投影).
虽然Jon 已经回答了使列表不可变的问题,但我还想指出,即使列表是不可变的,其包含的对象也不会自动不可变。
即使您使列表不可变(例如通过将其内容复制到 a 中ReadOnlyCollection<T>),您仍然可以操作所包含对象的属性,如果它们不是不可变类型。只要您传递对列表中包含的对象的引用,调用代码就可以操作这些对象。
让我们举个例子:
class Person
{
public string Name { get; set; }
}
class Group
{
private readonly IEnumerable<Person> _persons;
public Group(IEnumerable<Person> persons)
{
_persons = new ReadOnlyCollection<Person>(persons.ToList());
}
public IEnumerable<Person> Persons
{
get
{
foreach (var person in _persons)
{
yield return person;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们有以下代码:
List<Person> persons = new List<Person>( new[]{new Person { Name = "Fredrik Mörk" }});
Group smallGroup = new Group(persons);
Console.WriteLine("First iteration");
foreach (var person in smallGroup.Persons)
{
Console.WriteLine(person.Name);
person.Name += " [altered]";
}
Console.WriteLine("Second loop");
foreach (var person in smallGroup.Persons)
{
Console.WriteLine(person.Name); // prints "Fredrik Mörk [altered]"
}
Run Code Online (Sandbox Code Playgroud)
如您所见,即使我们使列表有效地不可变,*Person也不是不可变类型。由于类的Persons属性Group传递对实际Person对象的引用,因此调用代码可以轻松操作该对象。
保护自己免受这种情况的一种方法是将集合公开为IEnumerable<T>使用yield return和一些克隆机制,以确保您不会传递原始对象引用。例如,您可以将Person类更改为:
class Person
{
public string Name { get; set; }
// we add a copy method that returns a shallow copy...
public Person Copy()
{
return (Person)this.MemberwiseClone();
}
}
class Group
{
private readonly IEnumerable<Person> _persons;
public Group(IEnumerable<Person> persons)
{
_persons = new ReadOnlyCollection<Person>(persons.ToList());
}
public IEnumerable<Person> Persons
{
get
{
foreach (var person in _persons)
{
// ...and here we return a copy instead of the contained object
yield return person.Copy();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,上面的程序不会改变Person列表中实例的名称,而是改变它自己的副本。但是,请注意,我们现在拥有所谓的浅层不变性:如果Person反过来又会有不可变的成员,那么这些对象也存在同样的问题,依此类推……
Eric Lippert 在 2007 年就该主题撰写了由 10 部分组成的系列博客文章。第一部分在这里:C# 中的不变性第一部分:不变性的种类。
| 归档时间: |
|
| 查看次数: |
3045 次 |
| 最近记录: |