【问题标题】:Alternative to calling a virtual method in C#在 C# 中调用虚拟方法的替代方法
【发布时间】:2013-02-05 14:13:22
【问题描述】:

我在 C# 项目中使用 NHibernate,因此我有几个模型类。

让我们假设以下示例:

using System;

namespace TestProject.Model
{
    public class Room
    {
        public virtual int Id { get; set; }
        public virtual string UniqueID { get; set; }
        public virtual int RoomID { get; set; }
        public virtual float Area { get; set; }

    }
}

到目前为止,使用 NHibernate 映射这些对象工作正常。现在我想生成一个新的 Room 对象并将其存储在数据库中。为了避免单独设置每个成员,我向模型类添加了一个新的构造函数。 在我写的虚拟会员下面:

public RoomProperty()
{

}


public RoomProperty(int pRoomId, int pArea)
{
        UniqueID = Guid.NewGuid().ToString();
        RoomID = pRoomId;
        Area = pArea;
}

使用 FxCop 分析我的代码告诉我以下信息:

"ConstructorShouldNotCallVirtualMethodsRule"
This rule warns the developer if any virtual methods are called in the constructor of a non-sealed type. The problem is that if a derived class overrides the method then that method will be called before the derived constructor has had a chance to run. This makes the code quite fragile. 

This page 也描述了为什么这是错误的,我也理解。但我不知道如何解决这个问题。

当我删除所有构造函数并添加以下方法时...

public void SetRoomPropertyData(int pRoomId, int pArea)
        {
            UniqueID = Guid.NewGuid().ToString();
            RoomID = pRoomId;
            Area = pArea;

        }

.... 在调用标准构造函数后设置数据我无法启动我的应用程序,因为 NHibernate 初始化失败。它说:

NHibernate.InvalidProxyTypeException: The following types may not be used as proxies:
VITRIcadHelper.Model.RoomProperty: method SetRoomPropertyData should be 'public/protected virtual' or 'protected internal virtual'

但是将此方法设置为 virtual 与我在构造函数中设置虚拟成员时的错误相同。 如何避免这些错误(违规)?

【问题讨论】:

  • 为什么不在构造时为字段而不是属性设置值?
  • @voroninp 您无法使用 NHibernate 轻松访问字段
  • 因为我的模型实际上有大约 10 个成员,并且我创建了新的房间对象 qiet iften。我不想单独设置每个属性。
  • @Metalhead89 编码 sn-p(甚至 T4)不会有帮助吗?
  • 我不得不承认我不知道这是什么

标签: c# constructor virtual


【解决方案1】:

问题在于虚集。将值传递给基类构造函数中的虚拟属性将使用覆盖集而不是基集。如果重写集依赖于派生类中的数据,那么你就有麻烦了,因为派生类的构造函数还没有完成。

如果您绝对确定,任何子类都不会在覆盖集中使用其状态的任何数据,那么您可以在基类构造函数中初始化虚拟属性。考虑在文档中添加适当的警告。

如果可能,尝试为每个属性创建支持字段并在基类构造函数中使用它们。

您还可以将属性初始化推迟到派生类。为此,请在派生类的构造函数中调用的基类中创建一个初始化方法。

【讨论】:

    【解决方案2】:

    我希望以下其中一项工作:

    1. 使属性成为非虚拟的(只要 NHibernate 支持它就首选)。
    2. 从自动实现的属性更改为具有显式支持字段的属性,并在构造函数中设置字段而不是设置属性。
    3. 创建一个静态Create 方法,该方法首先构造对象,然后在返回构造对象之前为属性设置值。

    编辑:从评论中我看到选项 #3 不清楚。

    public class Room
    {
        public virtual int Id { get; set; }
        public virtual string UniqueID { get; set; }
        public virtual int RoomID { get; set; }
        public virtual float Area { get; set; }
    
        public static Room Create(int roomId, int area)
        {
            Room room = new Room();
            room.UniqueID = Guid.NewGuid().ToString();
            room.RoomID = roomId;
            room.Area = area;
            return room;
        }
    }
    

    【讨论】:

    • NHibernate 不支持非虚拟属性。而且我也无法将创建方法设置为静态,因为属性也必须是静态的,这不是我想要的(我也不知道这是否可行)
    • 我编辑了我的帖子以阐明选项 3。Create 方法是静态的,但属性不是。
    • 选项 3 如果你让构造函数受保护更有意义,所以构造 Room 实例的唯一方法是使用静态方法。
    【解决方案3】:

    恕我直言,好主意是制作基类 - abstract 及其构造函数 - protected。 接下来,inherited 类有它们的构造函数 private 和 - 对于外部世界 - 统一的 static 方法,如“Instance”,首先,初始化构造函数,然后 - 以正确的顺序调用整个类方法集,最后 - 返回类的 instance

    【讨论】:

      猜你喜欢
      • 2011-02-12
      • 1970-01-01
      • 2013-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-11
      相关资源
      最近更新 更多