【问题标题】:Is there a way to initialize members of a struct without using a constructor?有没有办法在不使用构造函数的情况下初始化结构的成员?
【发布时间】:2011-11-16 20:13:04
【问题描述】:

我有一个包含两个列表的struct:

struct MonthData
{
   public List<DataRow> Frontline;
   public List<DataRow> Leadership;
}

但是,我想在创建结构时初始化两者。如果我尝试:

struct MonthData
{
   public List<DataRow> Frontline = new List<DataRow>();
   public List<DataRow> Leadership = new List<DataRow>();
}

然后我得到:

Error   23  'MonthData.Frontline': cannot have instance field initializers in structs
...

由于结构不能有无参数构造函数,我也不能只在构造函数中设置它。到目前为止,我只能看到以下选项:

  1. 在创建 MonthData 实例时初始化这两个属性
  2. 使用类而不是结构
  3. 创建一个带参数的构造函数并使用它
  4. 为初始化它们的属性创建 getter 和 setter 懒洋洋的。

为此推荐的方法是什么?现在,我认为将其作为一门课程是最好的主意。

【问题讨论】:

  • 让它成为一个类。一旦将引用类型添加到结构中,它就不再是值类型了。
  • 将其设为引用类型(类)与值类型(结构)真的可以吗?如果是,请继续,让它成为一堂课。
  • 是的,这种类型作为结构没有意义。
  • 把它变成一个类会增加另一层间接性,如果它只包含两个类,最好将它作为值类型,因为每次通过某个地方时复制两个地址的开销很大多于一个间接
  • 结构在语言中是有充分理由的。在很多情况下,它们是设计新类型时的合适选择。至少从表面上看,这似乎是其中之一。

标签: c# struct


【解决方案1】:

你应该改用一个类。来自MSDN:

一般来说,类用于建模更复杂的行为,或在创建类对象后要修改的数据。结构最适合主要包含在结构创建后不打算修改的数据的小型数据结构。

【讨论】:

    【解决方案2】:

    无论如何,您在结构中使用引用类型 (List&lt;T&gt;),因此将结构用作值类型对我来说没有任何意义。我只是去上课。

    【讨论】:

    • 另一种选择是使用ImmutableArray&lt;T&gt; 并使用@CharlesBretana 提到的构建器方法。
    【解决方案3】:

    如果您只是询问语法...尝试构建并使用静态工厂...通常,结构应该用于不可变的事物,而工厂(调用私有构造函数)是与使用公共构造函数相比,不可变类型更好的方法。

      struct MonthData 
      {    
          public List<DataRow> Frontline;
          public List<DataRow> Leadership; 
          private MonthData(List<DataRow> frontLine = null, 
                            List<DataRow> leadership = null)
          { 
             Frontline = frontLine?? new List<DataRow>();
             Leadership = leadership?? new List<DataRow>();  
          }
          public static MonthData Factory(
              List<DataRow> frontLine= null, 
              List<DataRow> leadership= null)
          { return new MonthData(frontLine, leadership); }
      } 
    

    【讨论】:

    • 这绝对是一个有趣的方法。正如你所提到的,我的对象不是不可变的(它更像是一个集合,在它的整个生命周期中都会添加东西)所以也许一个类是最好的。为这个想法 +1。
    • 我喜欢静态工厂方法的想法。但是,您不能在结构中包含实例字段初始值设定项。所以public List&lt;DataRow&gt; FrontLine = new List&lt;DataRow&gt;() 不会编译。
    • 还要注意,即使你写的构造函数是私有的,默认的无参构造函数仍然是公有的。
    【解决方案4】:

    构造函数无法做到这一点。 CLR 可以并且将创建结构实例,只需将内存初始化为零并避免任何 ctor 开销。但是,您可以利用这些知识并创建具有相同可观察效果的延迟初始化属性。

    例如:

    struct MonthData {  
      private bool m_initialized;
      private List<DataRow> m_frontLine;
      private List<DataRow> m_leaderShip;
    
      public List<DataRow> FrontLine {
        get {
          EnsureInitialized(); 
          return m_frontLine;
        }
      }
    
      public List<DataRow> LeaderShip {
        get {
          EnsureInitialized(); 
          return m_leaderShip;
        }
      }
    
      void EnsureInitialized() {
        if (!m_initialized) {
          m_initialized = true;
          m_frontLine = new List<DataRow>();
          m_leaderShip = new List<DataRow>();
        }
      }
    }
    

    【讨论】:

    • 是的,我认为这样做是我的第二选择。但它是如此简单的对象,我倾向于同意这里的其他人并认为这应该只是一个类 - 特别是因为它只包含两个引用类型。
    • @MikeChristensen 通常是的,它可能应该是一个类。我的回答更像是“如果你被困在一个角落并且必须使用结构然后这样做”
    • 是的,我认为工厂方法和惰性初始化方法非常酷 - 我会记住这些,以应对未来的设计挑战。
    • 可变结构不是邪恶的,但是以语义上有意义的方式改变底层结构的属性获取器是。如果未初始化的 MonthData 存储在 List 之类的东西中,然后代码会重复执行“MyMonths(3).LeaderShip.Add(someDataRow);”,每次调用代码都会创建一个新的 List,向其中添加 someDataRow,然后丢弃新创建的列表。
    • @supercat 您对代码的解释不正确。属性延迟初始化一次,不重复初始化。
    【解决方案5】:

    如果 CLR 允许一种方法来保证结构类型的事物可以在它们对此类代码之外的任何东西可见之前运行一些初始化代码,那就太好了。即使在创建结构类型的数组时,也有可能做到这一点,方法是让 CLR 在对数组本身的引用在任何地方公开之前通过引用数组的所有单个元素传递。不幸的是,如果在创建数组的过程中抛出异常,就会导致尴尬的局面。如果构造函数没有任何副作用,就没有问题——只需丢弃数组并假装即使成功创建的对象也不存在。但是,具有副作用的构造函数可能会造成一些困难。

    然而,事实上,没有办法在构造时初始化结构,因此没有很好的方法来实现需要初始化的对象的值类型语义。对不起。即使值类型语义更适合您的类型(这种情况比某些人认为的要频繁得多),如果需要初始化,您也必须使用引用类型。结构的延迟初始化并没有真正起作用。例如,如果有一个名为 MyDict 的 Dictionary,重复访问 MyDict("George").FrontLine 将分别生成一个新列表。讨厌。

    可变结构不是邪恶的;如果有的话,我是他们更强有力的拥护者之一。尽管如此,.net 对可变结构的处理存在一些限制,因此在许多情况下值类型语义是合适的,但 .net 的限制使得无法正确提供此类语义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-09-27
      • 2020-03-22
      • 1970-01-01
      • 1970-01-01
      • 2022-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多