【问题标题】:Obtaining a reference to an instance whose constructor had thrown an exception获取对其构造函数抛出异常的实例的引用
【发布时间】:2016-06-25 07:29:49
【问题描述】:

考虑以下问题

在设计框架时,会呈现一个暴露某些事件的接口

interface I
{
  event MyEventHandler MyEvent
}

此接口最终将由许多不同的 3rd 方供应商实现,并可能被各种客户端使用。

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

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 废话,因为在我看来,在上帝的绿地上没有办法获得对这种实例的引用,但是问题是这种情况虽然很容易测试,但可能有我从未想过的极端情况,因此我提出了问题。

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

【问题讨论】:

  • 我真的不清楚你想问什么。首先,您在说什么“开销”?二、你到底想得到什么“参考”?
  • @CodeCaster 我已经编辑了我的问题。感谢您引起我的注意。开销是在事件参数类中为每个属性使用 Nullable API,引用是其构造函数抛出异常的引用。

标签: c# exception constructor defensive-programming


【解决方案1】:

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

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

这种模式到处都在使用。你已经用了很多次了,却没有意识到。例如,如果您说new FileStream(null),您将如何获得对该无效流的引用?你不能。

做正常的事。不过,您考虑到这一点很好。

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

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

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

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

还有一个问题:如果存在终结器,则会在该对象上调用终结器。终结器很少见,因为大多数时候非托管资源应该由句柄类持有。出于这个原因,这个问题很少生效。但是终结器可以看到其对象的私有状态。确保它可以处理这个问题。 FileStreams 终结器可能会检查中止的初始化并且什么都不做。

【讨论】:

    【解决方案2】:

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

    没有必要将属性设为Nullable<int>,因为您只能从构造函数中设置它(假设省略了private set),它不可为空。

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

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

    【讨论】:

    • 这是必要的,这样属性就不会使用默认值进行初始化,例如在属性域中可能有效的零。这就是为什么我首先把它放在那里的原因。至于“你没有得到一个实例”部分。你有什么具体的支持吗?我倾向于相信这一点,但我还没有找到具体的答案。
    • @Eyal stackoverflow.com/questions/10119896/…。目前我不能说任何关于可空设计的内容。
    • @CodeCaster 谢谢。
    【解决方案3】:

    回答你的问题。不,如果在构造函数中遇到错误,你不能得到 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 上使用这种方法有点压力,但是嘿:) 你总是可以从同一个通用抽象类中派生出所有这些

    希望这会有所帮助:)

    【讨论】:

    • 感谢您的更正,但是我看不出这对我有什么帮助?我试图确定的是是否可以获得实例,这将使无效的事件参数实例能够冒泡到客户端代码,尽管有安全机制..
    • 是的,解决方案是返回 new MyEventArgs(MyEventArgs.DEFAULT_ARG) 而不是 null
    • 这确实是一种解决方案,但我想避免它,因为这需要每个事件参数都具有这样的功能。另外,我的问题不需要解决方案,而更多的是以是/否和为什么的形式回答。我非常感谢您的努力。
    • 我很乐意提供帮助。问题是从构造函数返回空值是不可能的。这是我推荐的解决方法
    猜你喜欢
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 2011-11-04
    • 2013-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多