当工厂方法返回null时,我应该抛出什么异常?

时间:2012-03-14 16:53:41

标签: c# .net

考虑我可能控制或不控制的工厂方法,将其作为Func<T>传递给另一个类:

// this.FactoryMethod is an external dependency passed into this class
// through constructor or property injection assume that it is not null
// but there is no guarantee that it will return a non-null reference.
Func<IModel> FactoryMethod;

public IModel GetPopulatedModel(int state, FileInfo someFile)
{       
   // Argument validation omitted for brevity...

   // This operation could return null.
   var model = this.FactoryMethod()
   if (model == null)
   {
      throw new SomeException("Factory method failed to produce a value.");
   }

   // Safe to assign to properties at this point.
   model.Priority = state;
   model.File = someFile;
   return model;
}

我想抛出一些表明操作失败的东西。

ArgumentNullException会产生误导,因为这些论点没有错:

  

null引用时引发的异常(在Visual中为Nothing)   Basic)被传递给一个不接受它作为有效的方法   参数。

InvalidOperationException似乎并非如此:

  

方法调用无效时抛出的异常   对象的当前状态。

将局部变量视为the object's current state的一部分似乎很奇怪。

抛出什么是好的例外?我很惊讶OperationFailedException中没有System。我应该写自己的例外,还是有一个好的例外?

9 个答案:

答案 0 :(得分:5)

如果您认为可用的例外情况不充分,您可以随时创建自己的例外。

<强>更新 虽然,我同意@TomTom和@dtb关于可能的选项。特别是NotImplementedException因为工厂确实没有实施

答案 1 :(得分:3)

.NET Framework中的两个示例:

CloneCore 本质上是当前对象克隆的工厂方法。

我会使用 NotImplementedException ,因为工厂方法没有实现从它请求的内容。

答案 2 :(得分:2)

我会创建自己的异常,例如

  

FactoryNullException

然后你甚至可以创建你想要的默认信息,例如你的例子中的那个

Factory method failed to produce a value.

答案 3 :(得分:2)

InvalidOperationException异常。

局部变量定义了每个定义的对象状态。没什么不好。必须设置工厂方法,否则无法创建对象。 InvalidOperation - 工厂当时无法创建对象,因为工厂内部状态错误。

答案 4 :(得分:2)

NotImplementedException绝对不是这里的正确选择。 Its intended meaning is "not implemented yet",它几​​乎不会被生产代码抛出。

在这种情况下抛出什么的最重要的标准是对异常的消费者最有用的东西。如果您希望调用代码可能需要对此异常做出不同的反应,而不是任何异常,则异常类型非常重要。另一方面,调用代码对此类异常执行操作的唯一方法是记录它,它的消息非常重要,而且它的类型不那么重要。在后一种情况下,带有显式消息的InvalidOperationException可能非常合适。

还值得考虑是否应将工厂方法中的空值返回视为违反合同。在这种情况下你想要抛出异常的事实表明它可能应该是,在这种情况下你可能想要改变设计一点,以便注入一个接口实例而不是Func<>。这将允许您在接口方法上定义记录的合同,指定它永远不会返回null,并且如果它不能创建模型实例,它将抛出异常。如果您使用Code Contracts,您甚至可以将非null返回值作为其显式合约的一部分。

最后一点,如果你认为NullReferenceException是一个可以接受的东西(你对另一个答案的回答似乎表明),你可以通过简单地让model.Priority = state行执行来实现这一点。这个问题的唯一问题(GetPopulatedModel在从工厂接收到空模型实例后抛出异常的任何其他方法共享)是异常的源位置对于试图计算的开发人员不是特别有用为什么检索到的模型为null。这就是为什么“鼓励”工厂抛出自己的例外可能是最好的候选方法。

答案 5 :(得分:1)

为什么不使用InvalidOperationException(),因为如果失败则没有对所使用的方法的控制意味着它无效。

答案 6 :(得分:0)

也许

NullReferenceException

是你想要的。

答案 7 :(得分:0)

我不确定这是你在这里实施的工厂模式。但要回答你的问题,只需按照以下方式制作一个自定义异常:

public class FactoryNullException : Exception
{
    public FactoryNullException() : base("Factory was null")
    {

    }
}

答案 8 :(得分:0)

另一种选择:不要抛出异常。在实际应用程序中,从try / catch块中调用该方法,并且已捕获并记录此异常。在这种情况下抛出异常没有任何好处。

因此,我只是记录了消息并返回了null,而不是抛出异常。将其翻译为示例代码如下所示:

// this.FactoryMethod is an external dependency passed into this class
// through constructor or property injection assume that it is not null
// but there is no guarantee that it will return a non-null reference.
Func<IModel> FactoryMethod;

public IModel GetPopulatedModel(int state, FileInfo someFile)
{       
   // Argument validation omitted for brevity...

   // This operation could return null.
   var model = this.FactoryMethod()
   if (model == null)
   {
      this.Logger.Write("Factory method failed to produce a value.");
      return null;
   }

   // Safe to assign to properties at this point.
   model.Priority = state;
   model.File = someFile;
   return model;
}

如果抛出异常是必需的/有用的,我会选择NotImplementedException,但在这种情况下,不会抛出异常更有意义。