我的域类具有如下所示的集合:
private List<Foo> _foos = new List<Foo>();
public virtual ReadOnlyCollection<Foo> Foos { get { return _foos.AsReadOnly(); } }
Run Code Online (Sandbox Code Playgroud)
这给了我readonly集合,可以从类中修改(即使用字段_foos).
此集合映射如下(Fluent NHibernate):
HasMany(x => x.Foos).KeyColumn("ParentClassId").Cascade.All().Inverse().Access.CamelCaseField(Prefix.Underscore);
Run Code Online (Sandbox Code Playgroud)
现在当我尝试使用这个系列时,我得到:
无法转换'NHibernate.Collection.Generic.PersistentGenericBag 1[Foo]' to type 'System.Collections.Generic.List
1 [Foo]' 类型的对象.
根据Unable将NHibernate.Collection.Generic.PersistentGenericBag类型的对象强制转换为List,这是因为集合需要作为接口公开给NHibernate,以便NHibernate可以注入一个自己的集合类.
本文建议使用IList,但遗憾的是这个接口不包含AsReadOnly()方法,弄乱了我的计划,只向外部世界公开一个只读集合.
任何人都可以建议我可以使用哪种界面,一种满足相同要求的不同方法,或者不涉及这种挫折的另类职业?
谢谢
大卫
collections nhibernate fluent-nhibernate readonly-collection
我有方法将私有集合返回给调用者,我想阻止调用者修改返回的集合.
private readonly Foo[] foos;
public IEnumerable<Foo> GetFoos()
{
return this.foos;
}
Run Code Online (Sandbox Code Playgroud)
目前,私有集合是一个固定数组,但如果需要在运行时添加新项目,集合可能会成为一个列表.
有几种解决方案可以防止调用者修改集合.返回IEnumerable<T>
是最简单的解决方案,但调用者仍然可以将返回值向上转换IList<T>
并修改集合.
((IList<Foo>)GetFoos())[0] = otherFoo;
Run Code Online (Sandbox Code Playgroud)
克隆集合的明显缺点是有两个集合可以独立发展.到目前为止,我已经考虑了以下选项.
ReadOnlyCollection<T>
.Enumerable
通过执行类似的虚拟投影返回由类定义的LINQ迭代器之一list.Select(item => item)
.实际上我考虑使用Where(item => true)
因为返回的迭代器似乎更轻量级.我不喜欢使用ReadOnlyCollection<T>
它是它实现IList<T>
和调用Add()
或访问索引器将导致异常.虽然这在理论上是绝对正确的,但几乎没有真正的代码检查IList<T>.IsReadOnly
或IList<T>.IsFixedSize
.
使用LINQ迭代器 - 我将代码包装在扩展方法中MakeReadOnly()
- 防止这种情况,但它有一个黑客的味道.
编写自定义包装器?重新发明轮子?
有任何想法,考虑或其他解决方案吗?
在标记这个问题时,我发现了之前没有注意到的Stack Overflow问题.Jon Skeet也建议使用"LINQ hack",但使用效率更高Skip(0)
.
ReadOnlyCollection构造函数要求您为其提供IList.但是如果你有一些想要连接并产生新ROC的ROC,那么Concat方法将返回IEnumerable.哪个不是传递给ROC构造函数的有效参数.
那么如何创建一个ROC作为其他ROC的串联?
到目前为止,这是我能想到的最好的:
ReadOnlyCollection<T> ROCConcat<T> ( ReadOnlyCollection<T> a, ReadOnlyCollection<T> b)
{
List<T> tmp = new List<T>();
foreach (T f in a.Concat(b))
tmp.Add(f);
return new ReadOnlyCollection<T>(tmp);
}
Run Code Online (Sandbox Code Playgroud) 我正在尝试使用ReadOnlyCollection使对象不可变,我希望对象的属性是不可变的.
public ReadOnlyCollection<FooObject> MyReadOnlyList
{
get
{
return new ReadOnlyCollection<FooObject>(_myDataList);
}
}
Run Code Online (Sandbox Code Playgroud)
但我有点困惑.
我试图使用foreach将对象的属性更改为MyReadOnlyList并且...我可以更改value属性,是否正确?我理解ReadOnlyCollection设置一个添加级别,使对象不可变.
鉴于以下情况:如果 IReadOnly(T) 是从现有的 MutableEquivalent(T) 构想出来的,那么简单地转换为可变版本就允许完全访问,并且对其他人可能依赖的对象进行更改,这真的是这样吗?静态元素/数量?下面的输出显示了我的“THIRD INJECTION”字符串,尽管迭代了只读集合。难道只是出于礼貌和自我控制而禁止向下转换只读集合吗?您是否应该看到提供的接口,并承受其限制,强制转换不变,仅由于类型?感谢您对该界面和其他类似界面真正承诺/保证的任何澄清。
编辑-这不是 IReadOnlyCollection<T>
与 List.AsReadOnly() 的重复,因为我在问题中没有提到 List.AsReadOnly() ,也没有提到 IReadOnlyCollection 。这些是由响应者介绍的。这是一个关于 IReadOnly 接口后面的底层可变列表暴露的问题。一些评论中有一些重叠,但有一个问题,例如“IReadOnly 实际上对其底层可变列表(T)做了什么?” 重复了我的问题的精神。不是“这两个有关只读的实体之间有什么区别?”,因为我只提到了一个。
static class Program
{
public static void Main()
{
List<string> hack = (List<string>) READONLY;
hack.Add("THIRD INJECTION");
foreach (var s in READONLY)
{
Console.WriteLine(s);
}
}
public static readonly IReadOnlyList<string> READONLY = new List<string>{"@0", "@1"};
}
Run Code Online (Sandbox Code Playgroud) 考虑以下代码:
static int Main() {
byte[] data = File.ReadAllBytes("anyfile");
SomeMethod(data);
...
}
static void SomeMethod(byte[] data) {
data[0] = anybytevalue; // this line should not be possible!!!
byte b = data[0]; // only reading should be allowed
...
}
Run Code Online (Sandbox Code Playgroud)
有没有一种方法可以在C#中只读传递byte []?复制不是解决方案.我不想浪费内存(因为文件可能会变得非常大).请记住表现!
MSDN文章并没有真正解释这一点.
List<MyObject> FirstList = new List<MyObject>();
// Add items to FirstList.
List<MyObject> SecondList = new List<MyObject>(FirstList.AsReadOnly());
// Is SecondList a read-only collection?
Run Code Online (Sandbox Code Playgroud) 是否有 c# 语言构造允许我将项目添加到构造函数中的只读集合属性中?我想做这样的事情:
public class Node{
public IList<Node> Children {get; protected set;}
public Node(){
Children = new ObservableList<Node>();
}
}
Run Code Online (Sandbox Code Playgroud)
...在我的代码中的某个地方...
var node = new Node {Children.Add(new Node())};
Run Code Online (Sandbox Code Playgroud)
(不是一个很好的例子,但我希望这能传达这个想法)...
更新
好吧,抱歉,我需要说得更清楚。这门课不是我写的Node
,我也不能改变它。我问是否有 ac# 语言概念允许我添加到第二个片段中无参数构造函数中的只读集合,假设该类Node
不可更改。
我使用包含几个魔术字节序列的二进制格式。我想将它们作为不可变的静态成员保留在静态类中。
public static class HuffmanConsts
{
// output format: Header, serialized tree (prefix), DataDelimiter, coded data (logical blocks are 8 byte large, Little Endian)
public const string Extension = ".huff";
public static readonly IReadOnlyList<byte> Header = Array.AsReadOnly(new byte[] {0x7B, 0x68, 0x75, 0x7C, 0x6D, 0x7D, 0x66, 0x66}); // string {hu|m}ff
public static readonly IReadOnlyList<byte> DataDelimiter = Array.AsReadOnly(BitConverter.GetBytes(0L)); // eight binary zeroes, regardless of endianness
}
Run Code Online (Sandbox Code Playgroud)
ReadOnlyCollection<byte>
(从 返回Array.AsReadOnly()
)防止外部代码更改值,与byte[]
.
但是现在,我无法Header
通过输出stream.Write()
,因为它需要byte[]
:
stream.Write(HuffmanConsts.Header, 0, …
Run Code Online (Sandbox Code Playgroud) 我正在开发一个应用程序,它使用一个列表来处理用户以前的猜测.下面是(私人)列表和访问列表的属性.
为了防止隐私泄漏我正在使用ReadOnlyCollection,这也是我在属性中只有一个getter的原因(元素在类中直接添加到列表中,而不是通过属性).
现在来问题了.下面的代码生成错误消息:
怎么解决这个问题?提前致谢!
private List<int> _previousGuesses;
public ReadOnlyCollection<int> PreviousGuesses {
get {
return _previousGuesses;
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:好的,问题nr 2解决了(感谢Zortkun!).第一个问题是什么,我不能只使用吸气剂,任何想法?
我有这个示例代码.我想要做的是使"Nums"值只能使用"AddNum"方法写入.
namespace ConsoleApplication1
{
public class Person
{
string myName = "N/A";
int myAge = 0;
List<int> _nums = new List<int>();
public List<int> Nums
{
get
{
return _nums;
}
}
public void AddNum(int NumToAdd)
{
_nums.Add(NumToAdd);
}
public string Name { get; set; }
public int Age { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)
不知怎的,我已经尝试了一些关于AsReadOnly()和readonly关键字的东西,但我似乎无法让它做我想做的事情.
以下是我必须访问该属性的代码示例.
Person p1 = new Person();
p1.Nums.Add(25); //access 1
p1.AddNum(37); //access 2
Console.WriteLine("press any key");
Console.ReadLine();
Run Code Online (Sandbox Code Playgroud)
我真的希望"访问1"失败,"访问2"是唯一可以设置值的方式.在此先感谢您的帮助.
前段时间我发了一个关于参数的类似问题,它引用了一篇与这个问题更相关的文章,该文章提倡使用IReadOnlyCollection<T>
over IEnumerable<T>
。
最近,我一直在考虑回归的利弊
IEnumerable<T>
。从好的方面来说,它与接口一样小,因此它使您作为方法作者比承诺更重的替代方案(如
IList<T>
或(天堂禁止)数组)具有更大的灵活性。然而,正如我在上一篇文章中概述的那样,
IEnumerable<T>
返回会诱使调用者违反Liskov 替换原则。这太容易让他们使用LINQ扩展方法,如Last()
和Count()
,其语义IEnumerable<T>
不答应。需要的是一种更好的方法来锁定返回的集合,而不会使这种诱惑如此突出。(我想起了 Barney Fife 以艰难的方式学习这一课。)
输入
IReadOnlyCollection<T>
, .NET 4.5 中的新内容。它只向IEnumerable<T>
:Count
属性添加了一个属性。通过承诺计数,您可以向呼叫者保证您IEnumerable<T>
确实有终点站。然后他们可以Last()
问心无愧地使用 LINQ 扩展方法。
但是,本文提倡ReadOnlyCollection<T>
在您想要保护成员的情况下使用(和其他替代方法)。
所以,我和一个同事讨论过这个问题,我们有不同的意见。
他建议一个方法应该返回它所拥有的任何东西(例如 aList<T>
或数组),如果该方法产生它并且调用者之后更改它不会对其他任何东西产生影响(即,它不是成员并且不属于任何生命周期大于方法调用)。
我的观点是ReadOnlyCollection<T>
向调用者表明集合通常应保持原样。如果他们想改变它,那么他们有责任明确地将它转换为 aIList<T>
或调用ToList
它。
在微软的准则在特定的状态: …
我还在了解不变性以及何时使用这些对象,并开始在我的编码中加入更多的Lookup,因为我知道它们是不可变的,因此,通常比客户端可以更改的字典更好用.
话虽这么说,但我知道将ReadOnlyDictionaries引入.Net 有很多工作,我有点困惑,每一个都比另一个更有用,具体是什么让它们与众不同.我看过网上,但似乎找不到任何解释这种差异的文章.
例如,我看到了这个问题,并认为查找将是一个更理想的解决方案,但我很困惑为什么它不会.
任何人都可以指出我正确的方向/解释哪里将使用另一个.
谢谢!!
c# ×12
list ×4
collections ×3
.net ×2
immutability ×2
readonly ×2
arrays ×1
asp.net ×1
binaryfiles ×1
bytearray ×1
c#-4.0 ×1
class ×1
concat ×1
constructor ×1
filestream ×1
ienumerable ×1
lookup ×1
nhibernate ×1
return-type ×1