如何提供默认的公共构造函数并禁止在反射之外使用

本文关键字:反射 禁止 默认 何提供 构造函数 | 更新日期: 2023-09-27 18:35:45

我正在寻找一种方法来创建具有默认公共构造函数的类,但禁止在反射之外使用它。

更详细地说,我正在使用一个对象持久性框架(AWS DynamoDB - 我没有更改的选项),该框架要求对象实现这样的公共默认构造函数。该框架还要求所有持久化字段和属性也具有公共 get 和 set 访问器,并在创建和初始化新对象实例时使用它们。这会导致问题,因为访问器需要服务于双重目的 - 当框架使用它们时,它们需要"裸露",但在代码使用时需要强制实施限制和验证。无论如何,我已经解决了这部分,但它依赖于永远不允许代码使用默认构造函数(持久性框架使用的构造函数)构造持久化对象。

回到最初的问题 - 为了使这一切正常工作,我需要禁止代码使用默认构造函数,同时仍然使其可供持久性框架使用(仅搜索空的公共构造函数)。我首先使用静态标志并在任何潜在对象持久性使用之前和之后设置它,如果未设置,则在默认构造函数中抛出异常,但这开始在多线程环境(Web 服务器)中引起问题。一个线程会将其设置为由持久性框架使用,这将打开构造函数以供其他线程使用。我还尝试了一个 ThreadStatic 标志,但这有一个相反的问题,即阻止框架构造,因为它有时会生成工作线程。

现在我想知道是否真的有办法做到这一点。在这一点上,它更多地变成了一种好奇心而不是一种要求;我总是可以按照"不要使用此构造函数"的思路写一条评论,但直接执行规则会让我感觉更好。我以前使用过PostSharp,也许有一种方法可以使用编译时验证的一个方面来防止使用基于代码的默认构造函数?还有其他建议吗?

如何提供默认的公共构造函数并禁止在反射之外使用

使用 PostSharp,您可以使用体系结构约束来验证是否未从用户代码调用某些构造函数(执行 ReferentialConstraint 并将约束附加到类,可能使用多播和继承)。基本上,如果此代码的唯一合法使用来自反射,则在找到对构造函数的任何引用时可能会发出错误,期望从其他构造函数链接。

请注意,这不会阻止从new T()构造调用构造函数,其中 T 是泛型类型参数。所以只禁止新的运算符:从你的引用方面,使用ReflectionSearch.GetMethodsUsingDeclaration并过滤(Instructions & MethodUsageInstructions.NewObject) != 0

编辑:添加了有关ReflectionSearch.GetMethodsUsingDeclaration和MethodUsageInstructions的详细信息。

我不会花太多时间在这上面。不会有一种"干净"的方法可以做到这一点,你会浪费大量的周期来尝试。让调用者负责使用,只需明智地注释您的代码即可。

WinForms 中也会出现同样的问题,即要求窗体中的无参数构造函数支持 VS 设计器,而调用方在正常使用窗体时必须提供某些参数(例如,要显示的输入数据集)。

如果您确实想要,一种方法可能是在默认构造函数中使用反射来确定调用方,如果调用方程序集不是 DynamoDB,则引发异常;

if (!Assembly.GetCallingAssembly().FullName.Contains("DynamoDB"))
    throw new InvalidOperationException("BEGONE!");

另外,作为旁注,我通常将这些类型的"开发人员"检查实现为Debug.Asserts,而不是异常。通过这种方式,您可以在开发期间引入您所追求的保护,但在生产中(假设发布版本),断言不会编译,并且每次进行此调用时都不会造成(轻微)性能影响;

Debug.Assert(Assembly.GetCallingAssembly().FullName.Contains("DynamoDB"), "Invalid constructor usage");