Mat*_*ler 24 c# linq arrays data-structures
我想要得到的想法,这将是发布对象的只读目录作为一个公共方法的最佳方式?从Eric Lippert的博客来看,阵列有点糟糕,因为有人可以轻松添加新条目.因此,每次调用该方法时都必须传递一个新数组.他建议,要通过IEnumerable<T>,因为这是每个定义只读(没有添加,删除方法),我练习了很长一段时间.但是在我们的新项目中,人们甚至开始创建这些的数组IEnumerables,因为他们不知道后面的DataSource,所以他们得到:处理警告可能多次枚举IEnumerable
我对技术方法感兴趣,如何解决这个难题.到目前为止,我提出的唯一解决方案是使用a IReadOnlyCollection,但这比使用更明确IEnumerable.
发布此类列表的最佳做法是什么,这些列表不应更改,但应声明为内存列表?
Mat*_*zer 14
通常 - 并且从那时起 - 这解决了使用不可变集合.
例如,您的公共属性应该是类型IImmutableList<T>,IImmutableHashSet<T>等等.
任何IEnumerable<T>都可以转换为不可变集合:
someEnumerable.ToImmutableList();someEnumerable.ToImmutableHashSet();这样,您可以使用可变集合处理私有属性,并仅提供不可变集合的公共表面.
例如:
public class A
{
private List<string> StringListInternal { get; set; } = new List<string>();
public IImmutableList<string> StringList => StringListInternal.ToImmutableList();
}
Run Code Online (Sandbox Code Playgroud)
还有一种使用接口的替代方法:
public interface IReadOnlyA
{
IImmutableList<string> StringList { get; }
}
public class A : IReadOnlyA
{
public List<string> StringList { get; set; } = new List<string>();
IImmutableList<string> IReadOnlyA.StringList => StringList.ToImmutableList();
}
Run Code Online (Sandbox Code Playgroud)
检查IReadOnlyA 已经显式实现,因此可变StringList属性和不可变属性可以作为同一个类的一部分共存.
当你想暴露一个不可变的时候A,那么你将你的A对象返回到IReadOnlyA上层,而上面的层将无法StringList在上面的示例中改变整体:
public IReadOnlyA DoStuff()
{
return new A();
}
IReadOnlyA a = DoStuff();
// OK! IReadOnly.StringList is IImmutableList<string>
IImmutableList<string> stringList = a.StringList;
Run Code Online (Sandbox Code Playgroud)
它应该是一种可能的解决方案,以避免每次访问不可变的列表时将源列表转换为不可变列表.
如果项的类型覆盖Object.Equals和GetHashCode(可选)实现IEquatable<T>,则公共不可变列表属性访问可能如下所示:
public class A : IReadOnlyA
{
private IImmutableList<string> _immutableStringList;
public List<string> StringList { get; set; } = new List<string>();
IImmutableList<string> IReadOnlyA.StringList
{
get
{
// An intersection will verify that the entire immutable list
// contains the exact same elements and count of mutable list
if(_immutableStringList.Intersect(StringList).Count == StringList.Count)
return _immutableStringList;
else
{
// the intersection demonstrated that mutable and
// immutable list have different counts, thus, a new
// immutable list must be created again
_immutableStringList = StringList.ToImmutableList();
return _immutableStringList;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我不认为不可改变是要走的路
int[] source = new int[10000000];//uses 40MB of memory
var imm1 = source.ToImmutableArray();//uses another 40MB
var imm2 = source.ToImmutableArray();//uses another 40MB
Run Code Online (Sandbox Code Playgroud)
列表的行为方式相同.如果我想每次都进行完整复制,我不必关心用户对该数组的处理方式.使其不可变不会保护集合中对象的内容,它们可以自由更改.@HansPassant建议似乎是最好的
public class A
{
protected List<int> list = new List<int>(Enumerable.Range(1, 10000000));
public IReadOnlyList<int> GetList
{
get { return list; }
}
}
Run Code Online (Sandbox Code Playgroud)
对于您不打算修改的集合,IEnumerable<T>仍然可能是最安全的选项,而且它允许任何集合类型,而不仅仅是数组.这种警告的原因是因为IEnumerable代表使用延迟执行的查询的可能性,这意味着可能多次执行可能很昂贵的操作.
请注意,没有一个接口可以区分内存中的集合与可能的延迟执行包装器. 之前已经问过这个问题.
为修复这是不多次枚举源.如果代码需要执行多次迭代(这可能是合法的),那么将集合水合到迭代List<T> 之前.
IEnumerable是一个只读界面,隐藏了用户的实现.有人可以争辩说,用户可以转换IEnumerable为列表并添加新项目,但这意味着两件事:
IEnumerable描述行为,同时List是该行为的实现.当您使用时IEnumerable,您可以让编译器有机会将工作推迟到以后,可能会在此过程中进行优化.如果您使用ToList(),则强制编译器立即准备结果.
我使用IEnumerable每当使用LINQ表达式时,因为仅通过指定行为,我给LINQ提供了推迟评估并可能优化程序的机会.
要防止对List<T>对象进行任何修改,只能通过ReadOnlyCollection<T>不公开修改集合的方法的包装器公开它.但是,如果对基础List<T>对象进行了更改,则只读集合将反映MSDN中所述的那些更改.