如果调用代码只迭代集合,是否有任何理由将内部集合公开为ReadOnlyCollection而不是IEnumerable?
class Bar
{
private ICollection<Foo> foos;
// Which one is to be preferred?
public IEnumerable<Foo> Foos { ... }
public ReadOnlyCollection<Foo> Foos { ... }
}
// Calling code:
foreach (var f in bar.Foos)
DoSomething(f);
Run Code Online (Sandbox Code Playgroud)
正如我所看到的,IEnumerable是ReadOnlyCollection接口的一个子集,它不允许用户修改集合.因此,如果IEnumberable接口足够,那么就是要使用的接口.这是推理它的正确方法还是我错过了什么?
谢谢/ Erik
据我所知,.NET 4.0仍然缺少只读列表.为什么框架仍然缺乏此功能?这不是域驱动设计中最常用的功能之一吗?
Java对C#的优势之一就是Collections.unmodifiablelist(list)方法的形式,它似乎早在IList <T>或List <T>中已经过期了.
使用IEnumerable<T>
是问题的最简单的解决方案 - ToList
可以使用并返回副本.
Liskov替换原则要求子类型必须满足超类型的契约.根据我的理解,这将导致ReadOnlyCollection<T>
违反Liskov. ICollection<T>
合同公开Add
和Remove
运营,但只读子类型不符合本合同.例如,
IList<object> collection = new List<object>();
collection = new System.Collections.ObjectModel.ReadOnlyCollection<object>(collection);
collection.Add(new object());
-- not supported exception
Run Code Online (Sandbox Code Playgroud)
显然需要不可变的集合.有没有关于.NET的建模方法的事情?有什么更好的方法呢? IEnumerable<T>
在一个集合中做得很好,至少看起来是不可改变的.但是,语义非常不同,主要是因为IEnumerable
没有明确地暴露任何状态.
在我的特定情况下,我正在尝试构建一个不可变的DAG类来支持FSM.我显然在开始时需要AddNode
/ AddEdge
方法,但我不希望它一旦运行就可以更改状态机.我很难表示DAG的不可变和可变表示之间的相似性.
现在,我的设计涉及预先使用DAG Builder,然后创建一次不可变图,此时它不再可编辑.Builder和具体的不可变DAG之间唯一的通用接口是Accept(IVisitor visitor)
.我担心,面对可能更简单的选择,这可能是过度设计/过于抽象.与此同时,我无法接受我可以在我的图形界面上公开可能NotSupportedException
在客户端获得特定实现时抛出的方法.处理这个问题的正确方法是什么?
c# liskov-substitution-principle immutability readonly-collection directed-acyclic-graphs
我跑了一名安全码分析师,发现自己有一个CA2105警告.我查看了等级篡改示例.我没有意识到你可以将int []分配给readonly int.我认为readonly就像C++ const并使其成为非法的.
如何修复违规建议我克隆对象(我不想做)或'用无法更改的强类型集合替换数组'.我点击了链接并看到'ArrayList'并逐个添加每个元素,看起来你不能阻止添加更多内容.
所以,当我有这段代码时,最简单或最好的方法是什么才能使它成为只读集合?
public static readonly string[] example = { "a", "b", "sfsdg", "sdgfhf", "erfdgf", "last one"};
Run Code Online (Sandbox Code Playgroud) 考虑一个客户,公司,员工等具有ContactInfo属性的域,该属性又包含一组地址,电话,电子邮件等等......
这是我的缩写ContactInfo:
public class ContactInfo : Entity<int>
{
public ContactInfo()
{
Addresses = new HashSet<Address>();
}
public virtual ISet<Address> Addresses { get ; private set; }
public Address PrimaryAddress
{
get { return Addresses.FirstOrDefault(a => a.IsPrimary); }
}
public bool AddAddress(Address address)
{
// insure there is only one primary address in collection
if (address.IsPrimary)
{
if (PrimaryAddress != null)
{
PrimaryAddress.IsPrimary = false;
}
}
else
{
// make sure the only address in collection is primary
if (!Addresses.Any()) …
Run Code Online (Sandbox Code Playgroud) 我正在为MultiValueDictionary创建一个扩展方法来封装频繁的ContainsKey
检查,我想知道什么是创建空的最佳方法IReadOnlyCollection
?
我一直使用至今的new List<TValue>(0).AsReadOnly()
,但必须有一个更好的办法,一个equivilant来IEnumerable
的Enumerable.Empty
public static IReadOnlyCollection<TValue> GetValuesOrEmpty<TKey, TValue>(this MultiValueDictionary<TKey, TValue> multiValueDictionary, TKey key)
{
IReadOnlyCollection<TValue> values;
return !multiValueDictionary.TryGetValue(key, out values) ? new List<TValue>(0).AsReadOnly() : values;
}
Run Code Online (Sandbox Code Playgroud) 我正在使用selenium,我正在使用函数FindElements,所以我得到一个实现IReadOnlyCollection接口的元素.我想迭代列表,但似乎IReadOnlyCollection没有任何方法,如Get(int index)或操作[]的实现.
我想避免将结果转换为List或数组,因为我只想访问元素来读取它们.
目前我不想使用foreach,因为我需要管理索引,所以我可以将这些元素添加到另一个数组.
这就是我想要做的:
public void fillMatrix(){
IReadOnlyCollection<IWebElement> rows = Driver.FindElements(By.XPath("./*/tr"));
IReadOnlyCollection<IWebElement> elements;
matrix = new IControl[rows.Count()][];
for(int i = 0; i < matrix.Count(); ++i){
matrix[i] = rows[i].FinElements("./td").toArray();
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢
好的,所以List <>包含AsReadOnly(),它为您提供了ReadOnlyCollection.我需要的是有一个IList类型的字段,以及一个将为此列表返回ReadOnlyCollection的属性.
示例类:
class Test
{
private IList<Abc> list;
public AddToList(Abc someItem) { /* adds inside the list */... }
public ReadOnlyCollection<Abc> List { get { return ??? } } // <- no "set" here!
}
Run Code Online (Sandbox Code Playgroud)
方案如下:当项目添加到列表中时,我需要在我的类中有一些自定义逻辑,我想通过调用AddToList(someitem)来限制添加到此列表,同时不允许使用list.Add( someItem).问题是,我使用的NHibernate这需要IList接口,所以我不能投/呼吁的IList的AsReadOnly()(不包含此方法).
你会以什么方式来解决这种情况?我只需要一种方法让NHibernate以某种方式设置所需的集合,但我还需要限制用户.
我怀疑我对System.Collection.Generic.IReadOnlyCollection<T>
语义的理解,并怀疑如何使用只读和不可变等概念进行设计.让我通过描述两个性质,我怀疑之间的文件,其中规定
表示强类型的只读元素集合.
取决于我是否强调"代表"或"只读"(当我在脑海中发音,或者如果这是你的风格时大声说话),我觉得这句话改变了意思:
IReadOnlyCollection<T>
使用户(即对代表声明类型进行编码的人)明确表示他可能不会修改此集合.但是,声明类型本身是否可以修改集合是不明确的.第一个选项实际上是我的首选解释,尽管这个契约很容易被破解,例如通过ReadOnlyCollection<T>
从一个数组中构造一个T
然后将值设置到包装器数组中.
首创置业拥有门面不变性出色的接口,如IReadOnlyCollection<T>
,IReadOnlyList<T>
甚至可能IEnumerable<T>
等.但是,我觉得观察不变性也是有用的,据我所知,有没有在BCL carring这个意义上任何接口(请指出来如果我错了,请告诉我.有意义的是,这些不存在,因为这种形式的不变性不能通过接口声明强制执行,只能由实现者强制执行(接口可以承载语义,如下所示).旁白:我希望在未来的C#版本中拥有这种能力!
示例:(可以跳过)我经常需要实现一个方法,该方法将另一个线程使用的集合作为参数获取,但该方法要求在执行期间不修改集合,因此我将参数声明为类型,IReadOnlyCollection<T>
并给自己一个轻拍,认为我已满足要求.错误......对于一个调用者,签名看起来好像该方法承诺不更改集合,没有别的,如果调用者采用文档(外观)的第二种解释,他可能只是认为变异是允许的,并且方法在问题是对此的抵制.虽然此示例还有其他更常规的解决方案,但我希望您看到此问题可能是一个实际问题,特别是当其他人使用您的代码时(或未来 - 您就此而言).
所以现在我的实际问题(这引发了对现有接口语义的怀疑):
我想使用观察不变性和外观不变性并区分它们.我想到的两个选择是:
IImmutableCollection<T> : IReadOnlyCollection<T> { }
和IImmutableList<T> : IReadOnlyList<T> { }
.请注意,除了继承的接口之外,接口没有任何成员.这些接口的目的是单独说"即使声明类型也不会改变我!"‡我在这里特别说"不会"而不是"不能".这里有一个缺点:一个邪恶的(或者是错误的,保持礼貌的)实现者不会被编译器或其他任何东西阻止破坏这个合同.然而,优点是选择实现此接口而不是直接从其继承的接口的程序员很可能知道该接口发送的额外消息,因为程序员知道该接口的存在,因此可能会相应地实施它.我正在考虑使用第二种选择,但我担心它的设计问题与委托类型(它们被发明为在无语义的对应物上传递语义信息)相比Func
,Action
并且以某种方式失败,请参见此处.
我想知道你是否已经遇到/讨论过这个问题,或者我是否只是过多地讨论语义,应该接受现有的接口以及我是否只是不知道BCL中的现有解决方案.如上所述的任何设计问题都会有所帮助.但是我特别感兴趣的是你可能(已)提出我的问题的其他解决方案(简而言之就是在声明和使用方面区分观察和外观不变性).
先感谢您.
†我忽略了集合元素的字段等变异.
‡这对我之前给出的例子有效,但声明实际上更广泛.例如,任何声明方法都不会改变它,或者这种类型的参数表示该方法可以期望集合在其执行期间不会改变(这与说该方法不能改变集合不同,这是唯一的语句可以用现有的接口),也可能是许多其他语句.
有这个代码......
var b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 });
b[2] = 3;
Run Code Online (Sandbox Code Playgroud)
我在第二行遇到编译错误.我希望自ReadOnlyCollection<T>
实现以来有一个运行时错误,IList<T>
并且this[T]
在IList<T>
接口中有一个setter .
我试图复制ReadOnlyCollection的功能,但删除setter this[T]
是一个编译错误.
c# ×9
.net ×5
immutability ×2
readonly ×2
collections ×1
ienumerable ×1
ilist ×1
interface ×1
java ×1
liskov-substitution-principle ×1
listiterator ×1
nhibernate ×1
selenium ×1
semantics ×1
wcf ×1