获取对构造函数抛出异常的实例的引用

时间:2016-06-25 07:29:50

标签: c# exception constructor defensive-programming

考虑以下问题

在设计框架时,会出现一个暴露某个事件的界面

interface I
{
  event MyEventHandler MyEvent
}

此界面最终将由许多不同的第三方供应商实施,并可能被各种客户使用。

由于每个供应商都可能用无效数据新建事件args,因此作为框架作者的唯一控件是在事件args级别,所以我想到了以下模式:

class MyEventArgs
{

 public int? MyProperty{get;}

 MyEventArgs(int arg)
 {

   if(arg.IsInvalidArgument())//Let's pretend that there's such an extension method
     throw new ArgumentException(...)

   MyProperty = arg;
 }

这可以确保客户端不能使用某些流氓代码提供的无效值,因为构造函数会抛出异常,因此整数将没有赋值,使其成为空引用。

但是,这也会在客户端代码中产生开销,因为现在客户端必须检查 HasValue ,然后访问 Value ,这使得EventArgument不那么用户友好..这当每个事件参数的参数数量增加时,变得更加麻烦。

我可以在技术上删除问号,这会使客户无法理解 Nullable ,因为在我看来,上帝的绿地无法获得对此类的引用一个实例,但问题是这个场景虽然容易测试,但可能有我从未想过的边缘情况,因此我的问题。

是否有任何可能的方法来获取对构造函数抛出异常并将其传递给事件侦听器的实例的引用?

3 个答案:

答案 0 :(得分:3)

当构造函数抛出异常时,无法获取对该对象的引用(除非对象通过在抛出之前分发this 进行协作;不太可能,设计不好)

因此,您的无效值对象无法访问。它的状态确实无效(默认初始化),但没有人能看到它。它就像一个内部损坏的虚拟机,试图发射导弹,但你已经禁用了虚拟网卡。

这种模式遍布整个地方。你已经多次使用它而没有意识到。例如,如果您说new FileStream(null),您将如何获得对该无效流的引用?你不能。

做正常的事。不过,你想到这一点很好。

  

是否有任何可能的方法来获取对构造函数抛出异常并将其传递给事件侦听器的实例的引用?

没有。但这是一个可能的例子:

class C {
 public static C Instance; 
 public C() {
  Instance = this; //publish/leak
  throw ...;
 }
}

不要那样做。无论如何,这是不自然的代码。对象的构造函数通常不应该做太多,它应该使对象进入有效状态而不会引起副作用。副作用是发布this引用的唯一方法。

还有一个问题:如果存在一个终结器,将在该对象上调用终结器。终结器很少见,因为大多数时候非托管资源应由句柄类保存。因此,这个问题很少生效。但终结器可以看到其对象的私有状态。确保它可以解决这个问题。 FileStream的终结器可能会检查初始化中止而不执行任何操作。

答案 1 :(得分:0)

回答你的问题。不,如果在构造函数中遇到错误,则不能得到null结果

但是我认为最好的方法是在MyEventArgs类中添加一个静态构造函数

public static int DEFAULT_ARG = 1;//you can set this to whatever you want
public static Create(int arg)
{
    if(checkArg(arg))
    {
        return new MyEventArgs(arg);
    }
    return new MyEventArgs(MyEventArgs.DEFAULT_ARG);
}

然后使用var event = new MyEventArgs(arg);

而不是使用var event = MyEventArgs.Create(arg);的构造函数目录

在多个eventargs上使用这个方法有点压力,但是嘿:)你总是可以从相同的通用抽象类派生所有这些

希望这会有所帮助:)

答案 2 :(得分:0)

  

客户必须检查HasValue,然后访问Value

好吧,没有必要将属性设为Nullable<int>,因为你只能从构造函数(假设省略private set)设置它,它是不可为空的。 / p>

  

是否有任何可能的方法来获取对构造函数抛出异常的实例的引用

不,当构造函数抛出异常时,您不会返回实例。