代码之家  ›  专栏  ›  技术社区  ›  Harper Shelby damiankolasa

任何.NET窗体是否“正确”使用构造函数?

  •  7
  • Harper Shelby damiankolasa  · 技术社区  · 16 年前

    这在概念上与我的问题有关 here . 然而,我一直在玩NHibernate,并意识到我的问题的真正核心是什么。

    在经典的OO设计中,为了正确地封装数据,将值传递给对象的构造函数(存储在数据成员(字段)中是一种常见的模式。那些应该 be changed只使用访问器(只读属性)公开。允许更改的对象既有访问器也有赋值器(读写属性)。在我看来 适当的 O/RM应该遵守这些约定,并在创建对象时使用可用的构造函数。依赖于读写属性、反射或其他,hackish(imho)方法似乎…是错误的。

    有没有一个.NET O/RM解决方案可以做到这一点?

    编辑

    为了解决Praveen的观点,我知道有些项目有选择构造函数的“默认”算法-例如,结构映射总是使用参数最多的构造函数, 除非 使用自定义属性标记构造函数。我可以看到这是一个有效的方法来处理这种情况。或许使用IOC容器 除了 ORM将提供我所需要的解决方案——尽管在我看来,这并非本质上的糟糕,但对于使用ORM来说是不必要的额外步骤。

    8 回复  |  直到 16 年前
        1
  •  3
  •   Alex Kofman    16 年前

    我认为大多数ORM实际上支持这个概念,至少dataobject.net支持。此代码按预期工作:

    [HierarchyRoot(typeof(KeyGenerator), "Id")]
    public class Message : Entity
    {
      [Field]
      public int Id { get; private set; }
    
      [Field(Length = 100)]
      public string Text { get; private set; }
    
      public Message(string Text)
      {
        Text = text;
      }
    }
    

    编辑: 数据对象以内部事务状态存储数据,并使用由Postharp生成的特殊物化构造函数。当然,如果ORM使用POCO对象,它就不是那么简单了。

        2
  •  2
  •   Praveen Angyan    16 年前

    不幸的是,在.NET中,如果没有以某种方式标记构造函数,这是不可能的。

    为程序集元数据中的每个构造函数存储的方法签名只包含构造函数的每个参数的类型。任何.NET ORM都不可能真正知道要使用哪个构造函数。ORM看到的都是这样的:

    .ctor()
    .ctor(string, string)
    .ctor(string, string, string)
    

    例如,ORM无法知道哪个.ctor参数对应于客户对象的firstname、lastname和middlename。

    为了提供这种支持,.NET ORM必须支持读取为每个参数定义的自定义属性。您需要像这样标记构造函数:

    公共客户([property(“firstname”)]string firstname,[property(“lastname”)]string lastname,[property(“middlename”)]string middlename)

    这有两个缺点:

    1. 没有办法(我能想到,有人可能会纠正我),这可以进入一个映射文件。
    2. 您仍然需要像以前一样编写相同的映射,因为ORM仍然需要能够为每个属性获取单独的值。

    因此,您需要做所有这些额外的工作来标记构造函数,同时,您仍然需要像以前那样精确地映射类。

        3
  •  1
  •   Frederik Gheysels    16 年前

    你为什么认为这感觉不对? 您想让您的或/m在重新构建对象时执行业务逻辑吗?不,不。

    当您从数据库中加载一个对象时,或者/m应该能够重新构造该对象,不管是什么。在重新计算时设置一些值,不应导致触发某种逻辑来更改另一个值(也可能由ORM给定一个值…)

    尽管如此,我认为构造函数应该只包含那些在对象创建时强制要求对象处于“有效”状态的字段的参数。 除此之外,您可能还有一些公共只读属性,这些属性已通过某些计算给定值,并且需要持久化。
    如果你觉得反射是一种重组物体的“气味”,你将如何处理这种情况?您必须以某种方式创建一个公共方法,该方法能够设置“只读”值,但这会破坏封装,而且您也不会这样做。

        4
  •  1
  •   David Nelson    16 年前

    我同意前面的一个海报,即构造器应该在对象的生命周期开始时创建一个对象。使用构造函数对当前处于存档状态的现有对象进行水合物处理最多是违反直觉的。

    此时需要考虑的是,从存储在数据库或任何其他数据存储中的域对象的最后一个已知状态重新构造该对象,这本身并不是构造或修改操作;因此不应使用构造函数或属性设置器。相反,与这种操作最接近的框架机制是序列化!已经有了可识别的模式来存储对象的状态,然后通过ISerializable接口、标准序列化构造函数等重新构建它。将对象持久化到数据库与将对象持久化到流没有本质上的不同;实际上,streamingContextStates枚举的值之一在序列化过程中使用的是持久性!.imho,这应该是设计任何持久性机制时的标准方法。不幸的是,我不知道有什么ORM可以直接支持这个功能。

    还应该注意的是,设计用于序列化的对象仍然是POCO;持久性忽略没有受到侵犯。这里的关键点是,域对象应该最清楚需要什么数据来持久化和恢复它,以及应该以什么顺序恢复数据(这通常很重要)。对象不需要知道的是存储它的特定机制:关系数据库、平面文件、二进制blob等。

        5
  •  1
  •   Harper Shelby damiankolasa    16 年前

    天哪,我想我明白了!

    感谢大家的意见,但我要回答我自己的问题。我只是花了一个小时左右的时间在 PoEAA catalog 思考OO原则,并将其与对C语言和.NET框架的深入思考结合起来。

    我想到的答案是 我不能正确地使用构造函数来解决的需求,最终与构造函数本身没有关系。它是 lazy loading !

    基本上,如果不在域类中实现延迟加载(对于持久性、无知和灵活性来说,这是一个重要的禁忌),那么就无法在域类中进行子类化。这种子类化是NHibernate需要虚拟属性的原因。

    我仍然认为使用构造函数而不是反射或其他方法来填充父类的字段会更好(至少对于非集合来说……延迟加载确实有它的位置),但是我肯定看到了no-arg构造函数的位置。

        6
  •  0
  •   Daniel Brückner    16 年前

    在一般情况下,或映射器不可能只使用构造函数。假设一个对象由传递给构造函数的值初始化,然后通过方法调用修改状态。在这种情况下,对象可能被持久化为无效初始状态,因此被构造函数拒绝。

    所以可能有这样一个或映射器,但它肯定会有局限性。如给出的示例所示,我不认为通过或映射器绕过对象封装是一种糟糕的设计,而是在某些情况下作为一种需求。

        7
  •  0
  •   n8wrl    16 年前

    斯基特先生不久前提出了一个“建筑商”模式。我在波科班上上上了公共课。它与POCO具有相同的属性,但都是读/写的。POCO在必要的地方是只读的,但是是私有的。这样,当POCO被实例化并且没有用于构造函数的时髦参数时,构建器就可以设置属性。有点像“持久不变”。

        8
  •  0
  •   D'Arcy Rittich    16 年前

    这取决于您认为不应该更改的值。自动增量、计算列等都是很好的选择。

    当然是 可能的 ,我使用我编写的ORM,如果您试图设置只读属性的值,它将抛出异常。

    更新:

    记住,构造函数也用于持久化数据。让对象接受构造函数中的pk是一种常见的模式,它将自动获取该记录。