C#如何为集合创建公共getter和setter以及私有方法?

我想要一个带有(例如)SortedList集合“SrtdLst”属性的类“A”,并且在这个类“A”中允许添加或减少“SrtdLst”项。 但是在类“A”的实例中,只允许获取或设置项目的内容,而不是添加新项目或减去现有项目。 在代码中:

class A { public SortedList SrtdLst = new SortedList(); public A() { // This must work: SrtdLst.Add("KeyA", "ValueA"); // This too: SrtdLst["KeyA"] = "ValueAAA"; } } class B { public A a = new A(); public B() { // I want the following code to fail: a.SrtdLst.Add("KeyB", "ValueB"); // But this must work: a.SrtdLst["KeyA"] = "ValueBBB"; } } 

更新:我想创建一个类System.Data.SqlClient.SqlCommand。 对于存储过程,您可以使用填充“参数”集合的成员“DeriveParameters”,因此只能修改每个项目的值。

如何才能做到这一点?

如果要在编译时禁止修改操作,则需要类型安全的解决方案。

为公开允许的操作声明接口。 使用该接口作为属性类型。

 public interface IReadOnlyList { T this[int index] { get; } int Count { get; } } 

然后声明一个实现该接口的类,并inheritance自标准集合类。

 public class SafeList : List, IReadOnlyList { } 

假设您获得了正确的接口定义,您将不需要手动实现任何东西,因为基类已经提供了实现。

使用该派生类作为存储属性值的字段的类型。

 public class A { private SafeList _list = new SafeList(); public IReadOnlyList { get { return _list; } } } 

在A类中,您可以直接使用_list ,因此修改内容。 A类客户端只能使用IReadOnlyList提供的操作子集。

对于您的示例,您使用的是SortedList而不是List,因此接口可能需要

 public interface IReadOnlyDictionary : IEnumerable> { V this[K index] { get; } } 

我已经让它inheritance了IEnumerable,无论如何都是readonly,所以非常安全。 安全类将是:

 public class SafeSortedList : SortedList, IReadOnlyDictionary { } 

但除此之外它也是一样的想法。

更新:刚刚注意到(由于某种原因我无法理解)你不想禁止修改操作 – 你只是想禁止一些修改操作。 很奇怪,但它仍然是相同的解决方案。 无论您想要允许哪些操作,请在界面中“打开它们”:

 public interface IReadOnlyDictionary : IEnumerable> { V this[K index] { get; set; } } 

当然,现在界面的名称是错误的…为什么你想禁止通过添加添加但不通过索引器禁止它? (索引器可用于添加项目,就像Add方法一样。)

更新

从您的评论我认为您的意思是您希望允许分配现有键/值对的值,但不允许分配给以前未知的键。 显然,因为键是在运行时通过字符串指定的,所以在编译时无法捕获它。 所以你也可以去运行时检查:

 public class FixedSizeDictionaryWrapper : IDictionary { IDictionary _realDictionary; public FixedSizeDictionaryWrapper(IDictionary realDictionary) { _realDictionary = realDictionary; } public TValue this[TKey key] { get { return _realDictionary[key]; } set { if (!_realDictionary.Contains(key)) throw new InvalidOperationException(); _realDictionary[key] = value; } } // Implement Add so it always throws InvalidOperationException // implement all other dictionary methods to forward onto _realDictionary } 

任何时候你有一个普通的字典,你想把它交给一些你不信任更新现有值的方法,请将其包装在其中一个中。

编辑:原始答案如下。 正如耳语所指出的那样,我没有注意到你并没有要求它只是为了只读 – 只是为了防止Add操作。 这对我来说听起来不是一个好主意,因为Add和indexer-setter之间的唯一区别是如果元素已经存在,则Add会抛出exception。 无论如何,这很容易被呼叫者伪造。

为什么要限制一个操作?


原始答案

首先,不要使用公共字段。 这是遇到问题的绝对方法。

看起来你想要一个只读包装类围绕任意IDictionary 。 然后,您可以拥有一个返回包装器的公共属性,同时从类中访问私有变量。 例如:

 class A { private SortedList sortedList = new SortedList(); public IDictionary SortedList { get { return new ReadOnlyDictionaryWrapper(sortedList); } public A() { sortedList.Add("KeyA", "ValueA"); sortedList["KeyA"] = "ValueAAA"; } } 

现在你只需要找到一个ReadOnlyDictionary实现……我现在无法实现它,但如果有必要,我会稍后再回来……

只需将列表设为私有,并将其作为索引器公开:

 class A { private SortedList _list; public A() { _list = new SortedList() } public string this[string key] { get { return _list[key]; } set { _list[key] = value; } } } 

现在您只能使用索引访问项目:

 a["KeyA"] = "ValueBBB"; 

但是,由于列表的索引器允许创建新项目,因此如果您不希望这样做,则必须在索引器中添加代码以防止这种情况发生。

如果在类之外知道键,则可以将ChangeItem(key,newValue)和ReadItem(key)添加到包装类中。 然后将SortedList保持为类的私有。