【问题标题】:Class only writable by "parent" class, but readable by other classes类只能由“父”类写入,但可由其他类读取
【发布时间】:2010-08-29 03:24:27
【问题描述】:

我使用的是 C#,我没有太多经验(到目前为止,我主要使用 java/php/javascript)

我想要的是一个类,我在其中保存一些数据,这些数据只能被另一个类写入,但仍然可以被程序中的其他类读取。

类似这样的:

public class DataObtainer{
 DataItem[] Items;
 public DataObtainer(){
  Items = new DataItem[20];
 }
 public void Update(){
  Items[0].SomeProperty = 5;//Being able to change SomeProperty
 }
 //Class only contains properties
 public class DataItem{
  public int SomeProperty;
 }
}

public class AnyOtherClass{
 public void SomeMethod(){
  DataObtainer do = new DataObtainer();
  //What I want:
  DataItem di  = do.items[0];
  Console.WriteLine(di.SomeProperty);//Being able to read SomeProperty
  di.SomeProperty = 5;//Not allow this, not being able to change SomeProperty
 }
}

【问题讨论】:

    标签: c# access-modifiers


    【解决方案1】:

    使用接口。

    public interface IData
    {
       string Data1 { get;}
       int MoreData { get;}
    }
    
    class Data : IData
    {
       public string Data1 { get; set;}
       public int MoreData {get; set;}
    }
    
    public class DataObtainer
    {
       private Data[] items;
       public DataObtainer()
       {
          items = new Data[20];
       }
       public IEnumerable<IData> Items
       {
          get
          {
             return items;
          }
       }
    
       public void Update()
       {
          Items[0].MoreData = 5;//Being able to change MoreData
       }
    }
    
    public class AnyOtherClass
    {
       public void SomeMethod()
       {
           DataObtainer do = new DataObtainer();
           //What I want:
           IData di  = do.Items.First();
           Console.WriteLine(di.MoreData);//Being able to read SomeProperty
           di.SomeProperty = 5;//this won't compile
       }
    }
    

    说明:

    1. 创建要提供给代码 (IData) 的接口
    2. 在该接口的实现中创建
    3. 在获取器中存储实现
    4. 只允许其他代码访问接口。这不会让他们访问更改值。
    5. 调用代码可以根据需要强制转换为实现,但随后它们违反了合同并且所有赌注都没有了。

    【讨论】:

    • 如果您将具体实现设为私有内部类,其他类将无法转换为它
    • 非常感谢!正如 Rune FS 所说,我已将“Data”类设为私有内部类,并且一切都像魅力一样!不过,我还有一个问题,在您的示例中,您使用“IEnumerable Items”,我想知道这是否以及为什么这比“IData [] Items”更好?
    • @Wander,如果您授予他们访问 IData[] 的权限,那么他们可以接受并执行以下操作:obtainer.Items[1] = differentData;。我的假设是您不希望他们更改获取器中的对象。
    • @Rune FS,是的,这是真的。尽管我大多不担心人们会选择基本类型并违反合同。即使这样,如果他们真的想要,他们仍然可以使用反射来更改值。
    • 反射部分只能在定义组件和反射组件之间具有足够信任的情况下工作,因此换句话说,可以“密封它” IEnumeration 并未指定您不能更改顺序,但仅说明您不能也不应该始终依赖于能够做到。如果您返回一个数组,您仍然可以更改元素(是的,您需要强制转换)如果您执行了一个不可变序列,您的类型(可能与 IEnumerabl 一起定义合同)
    【解决方案2】:

    您应该使DataItem 成为一个外部(非嵌套)abstract 类,然后创建一个内部(私有)类来继承它并提供公共突变方法。

    DataObtainer 中,您可以将对象强制转换为私有继承类并进行修改。

    【讨论】:

      【解决方案3】:

      这种设计对我来说似乎很尴尬。您可以控制代码,因此除非您正在设计某种框架/api,否则您要求做的并不是真正必要的。如果一个类不应该能够修改属性,请不要修改该属性或不提供 setter。

      也许您能多解释一下您需要什么或为什么要完成此任务,以帮助我们了解在这里为您提供目标的最佳方法。

      使用基本继承的简单示例

      // Class used to read and write your data
      public class DataBuilder : Data {
          public void SetValue(int value) {
              base.m_SomeValue = value; // Has access to protected member
          }
      }
      
      // Class used to store your data (READONLY)
      public class Data {
          protected int m_SomeValue; // Is accessible to deriving class
          public int SomeValue {     // READONLY property to other classes
                                     // EXCEPT deriving classes
              get {
                  return m_SomeValue;
              }
          }
      }
      
      public class AnyOtherClass {
          public void Foo() {
              DataBuilder reader = new DataBuilder();
              Console.WriteLine(reader.SomeValue); // *CAN* read the value
              reader.SomeValue = 100; // CANNOT *write* the value
          }
      }
      

      【讨论】:

      • 我不同意。你不应该只记得不要设置一些东西。但是,语言提供了这种能力。事实上,这几乎是继承的最佳用例。
      • 这看起来很尴尬的原因是,如果您不需要设置某些内容,或者不想设置某些内容,那么它甚至不应该成为问题,因为它已经是设计的一部分。然而,如果这是一个框架或 API,那么它确实会带来安全/设计风险,其中可能会修改可能不应该修改的数据。但这就是属性的全部意义,隐藏存储数据的私有成员,以便您可以控制数据而不暴露这些类型的风险。继承,是的,是做这类事情的好方法,但我也没有质疑你的答案。
      • 非常感谢您的时间和帮助。我希望属性对其他类只读的原因是因为它只是在其他地方获得的数据的表示。更改属性不会更改其他地方的数据。如果我要更改其他类中的属性,它将不再是正确的表示,所以即使我不能在任何地方更改属性,我宁愿让代码根本不允许这样做。您的示例效果很好,但是我决定使用 tster 发布的接口方式,因为它需要较少的额外方法和属性。
      猜你喜欢
      • 2017-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-30
      • 1970-01-01
      • 2018-10-30
      • 1970-01-01
      相关资源
      最近更新 更多