【问题标题】:How to implement properties which can be set only one time如何实现只能设置一次的属性
【发布时间】:2018-09-03 09:26:49
【问题描述】:

我想实现一些只能设置一次的属性。 我知道 readonly 但我希望能够从类外部设置值。

情况如下: 我有一个类,我用它作为我从数据库中读取的一些数据的键。这个键类包含几个属性。

我的第一个想法是创建多个构造函数,我可以在其中设置我需要的属性,但由于“未设置”是大多数这些属性的有效状态,我很快就有了一堆构造函数,我认为这不是最好的方法。

我不能使用公共设置器创建属性,因为它们是关键信息,之后更改它们是没有好处的。所以我需要一种从键类外部设置它们一次的方法,因为我不想为每种情况制作 20 个稍微不同的构造函数。

是否有一些类似只读的易于使用的方法来执行此操作,还是我必须自己实现它?

【问题讨论】:

  • 这篇文章是专门针对 WPF 的,但你可能想偷一个 freezable object 的想法。
  • 你可能在谈论让你的班级单身吗?
  • 建造者模式?甚至可能使用“Fluent API”......比如KeyClass.WithPropA("someValue").WithPropC("someotherValue").Build()
  • @Fildor 我的想法完全正确。您可以在答案中对此进行解释吗?我应该吗?

标签: c#


【解决方案1】:

正如@Fildor 所评论的那样,Builder pattern 在这里可能是合适的。您可以根据需要在构建器类上设置属性,在那里允许多重分配没有害处。但是一旦你用它来构建你的类,它就会被锁定,然后就不允许再修改了。

class MyClass {
  internal MyClass(MyClassBuilder builder) {
     MyProp1 = builder.MyProp1;
     MyProp2 = builder.MyProp2;
     MyProp3 = builder.MyProp3;
  }
  public string MyProp1 { get; }
  public string MyProp2 { get; }
  public string MyProp3 { get; }
}

class MyClassBuilder {
  public string MyProp1 { get; set; }
  public string MyProp2 { get; set; }
  public string MyProp3 { get; set; }
  public MyClass Build() => new MyClass(this);
}

用法:new MyClassBuilder { MyProp2 = "abc" }.Build()。基于相同的总体思路,不同的 API 也是可能的。

【讨论】:

  • 如果 OP 使用某种映射器 API(例如 automapper)结合这个答案,代码会很酷。
【解决方案2】:

hvd 的构建器模式非常好,它是您最应该使用的。但是,这是我的 2 美分。我有时有点喜欢 JS 的工作方式,在这里我尝试模仿它。这基本上是使用字典作为配置集合,包含属性的名称和所需的值。

public class Key
{
    private readonly string Property1;
    private readonly string Property2;
    private readonly string Property3 = "!";

    public Key(Dictionary<string, object> config)
    {
        if (config == null)
        {
            return;
        }

        var type = typeof(Key);
        var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
        foreach (string name in config.Keys)
        {
            var field = fields.Where(f => f.Name == name).SingleOrDefault();
            if (field == null)
            {
                continue;
            }

            field.SetValue(this, config[name]);
        }
    }

    public string GetValues()
    {
        return Property1 + Property2 + Property3;
    }
}

您可以将其用作:

var key = new Key(new Dictionary<string, object>()
    {
        { "Property1", "Hi, " },
        { "Property2", "There" }
    }
);

Console.WriteLine(key.GetValues()); // Hi, There!

【讨论】:

    【解决方案3】:

    解决此问题的一种方法是使用公共 setter 属性并为每个属性设置一个标志,第一次调用 setter 将标志设置为 true 并通过在后续调用中检查标志从 setter 返回。

    如果您有太多属性,请使用字典而不是将属性映射到状态(设置/取消设置)。

    【讨论】:

      【解决方案4】:

      尝试这样的事情(基本上是@SelmanGenc 的建议):

      public class Key {
      
      private ConcurrentDictionary<string, bool> _setProps = new ConcurrentDictionary<string, bool>();
      private string _prop1;
      
      public string Prop1 {
      
      get { return _prop1; }
      set { 
          if(!_setProps.ContainsKey(nameof(Prop1)){
              _setProps.Add(nameof(Prop1), true);
              _prop1 = value;
          }
        }
       }
      }
      

      【讨论】:

      • @pere57 真的取决于用例:)
      • @pere57 如果您愿意,可以在else 中抛出异常。
      【解决方案5】:

      有一个知道它是否被设置的私有属性? 类似:

      public class MyClass
      {
         private bool myValueSet = false;
         private string _myValue;
      
         public string MyValue
         {
            set {
              if (!myValueSet)
              {
                _myValue = value;
              }
            }
            get {
              return _myValue; 
            }
         }
      }
      

      【讨论】:

      • 这与zaitsman answer 类似,但他/她的回答允许多个属性(更容易维护)
      • @MarkRedman 是的,但您不知道它已被忽略。您必须阅读并检查...
      • @pere57:我在原帖中没有看到这是要求。
      猜你喜欢
      • 2016-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-02
      • 2021-12-12
      • 2015-12-18
      • 2012-05-10
      • 2011-04-24
      相关资源
      最近更新 更多