【问题标题】:How to tell when object initializer is done如何判断对象初始化器何时完成
【发布时间】:2012-02-09 04:33:49
【问题描述】:

我有各种派生对象,我希望用户能够使用对象初始值设定项。我有一个“Initializing”属性,我希望在设置这些字段时将其设为 true,然后我希望 Initializing 属性随后设置为 false。

我如何知道对象初始化器何时完成才能执行此操作?

class Foo
{    
    Public Foo(string p1, string p2)
    {
        Initializing = true;
        Property1 = p1;
        Property2 = p2;
        Initializing = false;
    }

    bool Initializing;

    string _property1;
    string Property1 
    {
        get { return _property1; } 
        set { _property1 = value; DoSomething(); }
    }

    string Property2 { get; set; }

    public void DoSomething()
    {
        if(Initializing) return; // Don't want to continue if initializing
        // Do something here
    }
}

在上面的例子中,如果你使用构造函数,它就可以正常工作。问题是如何使它与对象初始化器以相同的方式工作。

编辑:对于所有反对者,这里有其他人正在寻找我所追求的东西 - http://blogs.clariusconsulting.net/kzu/how-to-make-object-initializers-more-useful/

不幸的是,它看起来确实是不可能的。

【问题讨论】:

  • 你能发布一些代码来更好地说明你想要做什么吗?
  • @BrandonMoore 这真是个坏主意。你真的让我不寒而栗。
  • @BrandonMoore - 这将为您赢得一票否决票(和一个评论标志)。
  • @BrandonMoore 主要是因为我不知道您是如何进入这种情况的,也不知道您的实际项目受到的影响有多大。因此,对于可能需要对我一无所知的项目进行大量重组的建议,我犹豫不决。
  • @BrandonMoore 很抱歉,您不喜欢得到 Bradon 的答案。问题是你真的把自己逼到了一个角落。主要问题是您使用属性设置器做的事情不应该依赖于设置属性的顺序。不过,您的 ctor 代码很好,您可以设置后备存储,并以您选择的任何方式调用副作用。我只是担心由于误解,您可能会遇到更好的问题。猛烈抨击无济于事,这里的人们通常非常乐于助人和友好,并且不妨碍他们

标签: c# oop object-initializers


【解决方案1】:

如果您确实需要跟踪对象的初始化,那么您需要手动实现该逻辑。一种方法是复制 WinForms 代码生成器使用的内容。当对象希望批量更新属性时,它们会公开ISupportInitialize 接口。所以用法类似于...

var x = new Foo();
x.BeginInit();
x.Property1 = 1;
x.Property2 = 2;
x.EndInit();

【讨论】:

  • +1 作为这里唯一有建设性的人并发布一些有用的东西,即使它对我的情况没有多大帮助。我想我只需要找到一种更好的方法,因为我想要的基本上是将对象初始化器视为某种构造函数,即使我知道它不是这样。
  • @BrandonMoore:我也打算回答这个问题。 这就是你的做法。你应该检查并抛出 BeginInit/EndInit 是否没有被调用或没有以正确的顺序被调用。唯一的另一种选择是提供一个 ctor,它采用包含初始化类型所需的所有数据的类型。这是在框架中发现的另一种模式(查看 AppDomain 作为相关示例)。这就是答案,所以你不需要再问任何东西,除了一个新问题。
  • @Will 谢谢。显然不是我希望听到的,但它就是这样:/
【解决方案2】:

设置任何标志都没有意义。在初始化程序运行之前,您无法访问对象。例如:

var object = new object() { Prop1 = "Boo" }

由于在设置 Prop1 之后才能访问从 new 返回的引用,因此无法访问任何属性,因此无需控制访问或担心它是否“完成”。

不过,我想我可以看到你可能会有这样的事情:

public class Foo {
    private int _value;
    public int Bar {
        set {
            _value = value * Baz; // don't want to do this if initializing
        }
    }

    public int Baz { get; set; }
}

如果这是您所关心的,那么您的对象设计不正确。属性不应该有那样的副作用。无法知道是否所有初始化都已完成。

【讨论】:

    【解决方案3】:

    由于对象初始化只是语法糖,您无法区分它与普通属性集之间的区别。我也想不出一个合理的案例来区别对待他们。

    也就是说,如果您说必须设置至少 1 个 x 属性(无论是速记还是速记语法),那么您可以在 ctor 中将 initializing 设置为 true,然后在每个属性集上将其设置为 false。

    【讨论】:

      【解决方案4】:

      这个问题毫无意义。对象初始化器语法只是语法糖的简写。

      这个:

      var myInstance = new someClass()
      {
          Prop1 = "",
          Prop2 = "",
          Prop3 = "",
          Prop4 = "",
          Prop5 = ""
      }
      

      和这个完全一样:

      var myInstance = new someClass();
      myInstance.Prop1 = "";
      myInstance.Prop2 = "";
      myInstance.Prop3 = "";
      myInstance.Prop4 = "";
      myInstance.Prop5 = "";
      

      没有检测到“完成”。

      你想做的事情我们可以做到:

      class someClass()
      {
        public string AStringProperty { get; set; }
        public bool IsInitiazlied 
        {
          return string.IsNullOrWhitespace(this.AStringProperty);
        }
      }
      

      或者,让 ctor 采用值的初始状态,然后保证设置。

      class someClass()
      {
        public string AStringProperty { get; set; }
        public someClass(string AStringPropertyInit)
        {
            this.AStringProperty = AStringPropertyInit;
        }
      }
      

      编辑

      class Foo
      {    
          Public Foo(string p1, string p2)
          {
      
              _property1= p1; //set the backing store directly, 
                              //skips the side effect in the setter
              Property2 = p2;
      
              DoSomething(); // now cause the side effect
                             // we know everything is setup
          }
      
      
      
          string _property1;
          string Property1 
          {
              get { return _property1; } 
              set { _property1 = value; DoSomething(); }
          }
      
          string Property2 { get; set; }
      
          public void DoSomething()
          {
              // Do something here
          }
      }
      

      【讨论】:

      • 这个词是“语法糖”;)
      • @asawyer 感谢您终于尝试提供帮助。如果属性是从初始化程序设置的,我不希望 DoSomething() 中的内容运行,但如果不是从初始化程序调用它们,我会这样做。这个想法是,当我初始化对象时,它处于“初始”状态。但是当属性发生变化时,我实际上希望 DoSomething() 运行。所以这个解决方案还不能完全解决这个问题。
      • @BrandonMoore 当你说初始化器时,你是指构造器吗?
      • (叹气)不。我只是把构造函数放在那里作为我正在寻找的行为的一个例子。但是由于其他限制(例如类在泛型类型中的使用),我不想拥有除默认公共构造函数之外的任何构造函数。
      • @BrandonMoore 请参阅 Phil Wright 的回答。你需要做这样的事情,我几乎可以保证这会比它的价值更令人头疼。
      【解决方案5】:

      您可以让DoSomething 方法验证它是否具有完成其工作所需的信息,而不是依赖显式属性来告诉您对象何时初始化。您的示例非常粗略,我希望您的实际实现更复杂,所以我假设必须将 Property1Property2 分配给 something (意味着在继续之前不是空字符串):

      class Foo
      {    
          public Foo(string p1, string p2)
          {
              Property1 = p1;
              Property2 = p2;
          }
      
          string Property1 { get; set; } 
          string Property2 { get; set; }
      
          public void DoSomething()
          {
              // *** Replace this with something valid to your real code
              if(!string.IsNullOrEmpty(Property1) || !string.IsNullOrEmpty(Property2)) 
                  return; // Don't want to continue if not initialized
              // Do something here
          }
      }
      

      更新

      不了解您实际使用的对象模型,这里有一个基于在整个框架中广泛使用的模型的可能替代方案:

      class Foo
      {
          public void DoSomething(FooConfig config)
          {
          }
      }
      

      class Foo
      {
          private FooConfig config_;
      
          public Foo(FooConfig config)
          {
              config_ = config;
          }
      
          public void DoSomething()
          {
          }
      }
      

      FooConfig 的定义位置:

      class FooConfig
      {
          public string Property1 { get; set; }
          public string Property2 { get; set; }
      }
      

      DoSomething 使用以下方式调用:

      (new Foo()).DoSomething(new FooConfig() { Property1 = "abc"; Property2 = "def"; });
      

      (new Foo(new FooConfig() { Property1 = "abc"; Property2 = "def"; })).DoSomething();
      

      这可以很容易地更改以适应 FooFooConfig 上的构造函数使用。

      【讨论】:

      • 问题是如果属性已“更改”,我希望 DoSomething 运行。但是,如果对象是使用属性初始化的,我不希望 DoSomething 运行。
      • @BrandonMoore - 这有点令人困惑。所以你想让DoSomething在属性有值的情况下运行,只要它没有通过属性的set方法显式设置?
      • 嗯......显然,如果它是从初始化程序设置的,那么它是通过 set 方法设置的,尽管我明白你想说什么。除了问题不在于它是否通过设置器“显式”设置,而是它是否通过初始化器设置。在我目前的方法中,这两件事似乎是同义词,但不同之处在于,对于我实际尝试做的事情,也许有更好的方法,而另一件事可能没有。我“不必”按照我提议的方式去做,如果有更好的设计,我会全力以赴。
      • @BrandonMoore - 这几乎是我回答的重点。 DoSomething 不关心对象是否已初始化(可以说它不应该),它只关心是否具有执行任务所需的信息。我会用一个可能会稍微改善这种情况的替代方案来更新我的答案。
      • 那么,如果 DoSomething 的目的包括保存属性的最后状态,你会认为我不应该这样做吗? ;)
      【解决方案6】:

      对象初始化器只是创建对象并在其上设置一堆属性的简写。问“什么时候完成”是没有意义的。

      如果您希望对象的状态依赖于正在初始化的特定属性集,那么您可以将代码添加到每个属性的设置器并在内部跟踪它。不过,这实际上取决于您要达到的目标。

      更新

      只是在@asawyer 的回答中阅读您的cmets。无法判断是否在初始化程序中设置了属性。只是为了说明 - 这里有三段代码,它们做的事情完全相同,只是你似乎希望每段代码都有不同的行为。

      var someVar1 = new SomeClass()
                     {
                          Prop1 = "value1";
                          Prop2 = "value2";
                     };
      
      var someVar2 = new SomeClass()
                     {
                          Prop1 = "value1";
                     };
      someVar2.Prop2 = "value2";
      
      var someVar3 = new SomeClass();
      someVar3.Prop1 = "value1";
      someVar3.Prop2 = "value2";
      

      跟踪单个属性设置器的执行并不难,并且除了第一次执行之外都会触发一些副作用,但是说我只想在客户端更改初始对象配置时触发副作用ctor 完成后就没有意义了。

      【讨论】:

      • 不要将限制等同于逻辑。不能做某事并不意味着可以做就没有意义。
      • @BrandonMoore 不过,重点是在 C# 中,可能在大多数 OO 语言中,对象的初始状态是构造函数完成后的状态。如前所述,对象初始化器只是用于设置一堆属性的语法糖。使用默认构造函数和显式属性调用也可以完成同样的事情。如果你想强制你的类的用户在对象被认为初始化之前设置一组特定的属性,那么你最好在构造函数中编码而不是默认构造函数。
      猜你喜欢
      • 2019-10-10
      • 2018-08-05
      • 2021-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多