如何提供默认的公共构造器并禁止使用外部反射

时间:2012-05-29 22:47:14

标签: c# constructor postsharp

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

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

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

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

2 个答案:

答案 0 :(得分:1)

我不会花太多时间在这上面。没有一种“干净”的方式去做,你会浪费大量的周期尝试。让调用者负责使用,只需明智地评论您的代码。

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

如果你真的想要,一种方法可能是在默认构造函数中使用Reflection来确定调用者,如果调用者程序集不是DynamoDB则抛出异常;

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

另外,作为旁注,我通常将这些“开发人员”检查实现为Debug.Asserts,而不是Exceptions。通过这种方式,您可以在开发过程中引入所需的保护,然后在生产中(假设发布版本),不会编译断言,并且每次进行此调用时都不会产生(次要)性能损失;

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

答案 1 :(得分:1)

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

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

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