使具有公共属性的对象的列表/集合真正成为只读

时间:2015-04-30 06:01:13

标签: c# immutable-collections

我有以下问题。

让我们说有一个像这样定义的公共Class1:

public class Class1
{
     public string pr1 { get; set; }
     public string pr2 { get; set; }

     public Class1()
     {
     }
}

我不允许修改类定义 - 它被其他人使用,他们需要以这种方式编写。这就麻烦了。

我需要static List<Class1> myStaticList,只能在Class2中进行修改,其他地方都是真正的只读。 并且通过真正的只读&#39;我的意思是用户不得更改任何内容,包括列表元素的属性。

应该阻止从Class2外部调用的Class2.list[0].pr1="newValue"

ReadOnlyCollection没有工作,因为用户仍然可以修改列表的给定元素, 所以这里给出的解决方案List<T> readonly with a private set并不能满足我的需求。

基本上我发现了很多可能的列表/集合只读方式(例如IReadOnlyList)但是如果它们有公共setter,它们都有可能改变给定列表元素的属性。

我发现的唯一解决方案是创建新课程:

public class Class1ReadOnly : Class1
{
    public new string pr1{ get; private set; }
    public new string pr2{ get; private set; }
    public Class1ReadOnly(Class1 tmp)
    {
        pr1= tmp.pr1;
        pr2 = tmp.pr2;
    }
}

但我希望两个找到更好的方法,因为在我看来因为一个单一的只读列表来定义整个新类时有点奇怪

提前感谢任何线索。

这甚至可能以这种不同的方式吗?

真实世界的背景是这样的:

在代码中我们编辑数据库,我希望有一些记录的备份列表,这些记录已经用它们的初始值进行了更改。我想让它静态只是为了有可能在我想要的任何地方恢复数据库。 Class1包含我们数据库的单个记录的所有值,因此List<Class1>我可以保留所有已修改的记录,并在需要时恢复它们。由于它是备份,因此必须是只读的,因为意外的更改可能会导致灾难。

3 个答案:

答案 0 :(得分:2)

我不允许更改它&#34;,你的意思是(正如问题措辞暗示的那样)你不允许修改课程中的值吗?或者你真的不允许修改类声明本身?此外,如果您不允许修改类声明,那么在集合中使用该类的代码呢?例如。是List<Class1>对象需要继续为List<Class1>,还是可以用其他内容替换Class1类型?


如果没有更多的背景,很难确切知道什么是最好的。但是,一种可能的方法是将Class1替换为接口:

interface IClass1
{
    string str1 { get; }
    string str2 { get; }
}

然后将Class1声明为实现该接口,嵌套在Class2

class Class2
{
    private class Class1 : IClass1
    {
        public string str1 { get; set; }
        public string str2 { get; set; }
    }
}

现在,使用您已经看到的使列表本身为只读的技术,可以防止任何其他代码替换列表中的元素(即使用某些其他实现接口)。只有Class2可以成功地将列表中的IClass1元素的实现投射到Class1,以获取对属性的公共setter的访问权。

如果这没有解决您的问题,那么如果您可以更准确地了解解决问题的确切约束条件将会有所帮助。

答案 1 :(得分:2)

鉴于你的方法,你的方法是有效的。您正在使用适配器模式的解决方案。做这样的事情并不奇怪

  

适配器模式
  意图
  将类的接口转换为客户期望的另一个接口。

   - 设计模式(ISBN 0-201-63361-2)

我会定义这样一个类:

public interface IClass1 {
  string pr1 { get; }
  string pr2 { get; }
}

internal class Class1Adapter : IClass1 {
  public static Class1Adapter FromClass1(Class1 class1) {
    var pr1 = class1.pr1;
    var pr2 = class1.pr2;
    return new Class1Adapter {pr1 = pr1, pr2 = pr2};
  }

  public string pr1 { get; private set; }
  public string pr2 { get; private set; }
}

为什么要做出我的选择?

  • 内部类所以我不泄漏到其他程序集。这样,除非您明确表达,否则其他团队无法使用Class1Adapter
  • 实施新界面IClass1。你说你无法改变Class1的定义,但如果它不是API破坏变化......你可以将它推荐给其他团队。您可以使用当前定义更改Class1来实现IClass1,而无需进行其他更改,您可以在代码中使用:
var list = new ReadOnlyCollection<IClass1>(source); 

// ... and later, if attempted

list[0].pr1 = "I'm breaking you!"; 

会产生:

  

无法将属性或索引器“ConsoleApplication19.IClass1.pr1”分配给 - 它是只读的

答案 2 :(得分:1)

如何受保护设置?

https://msdn.microsoft.com/en-us/library/bcd5672a.aspx?f=255&MSPPError=-2147217396

或者您可以覆盖属性设置器。

:)