.NET:未设置必需属性时要引发哪个异常?

.NET:未设置必需属性时要引发哪个异常?,.net,exception,dependency-injection,.net,Exception,Dependency Injection,假设我有一个这样的类: public class Foo { public Bar RequiredProperty { get; set;} public void Baz() { if (this.RequiredProperty == null) { // Which exception should I throw? } } } var formatter = new HtmlCo

假设我有一个这样的类:

public class Foo
{
    public Bar RequiredProperty { get; set;}

    public void Baz()
    {
        if (this.RequiredProperty == null)
        {
            // Which exception should I throw?
        }
    }
}
var formatter = new HtmlCodeFormatter()
    {
        Parser = new CSharpParser();
        Formatter = new HtmlCodeFormatter();
    };
formatter.Format("Console.WriteLine(\"Hello, world!\"));
我的解决方案有一个类,该类被设计为可以重用,而无需反复向
Bar
方法传递大量参数。那么,当
Bar
未初始化为非空值时,我应该抛出什么呢

更多信息 实际上,我正在运行自己的代码解析器和格式化程序。称之为实物课程。其中一个类是
HtmlCodeFormatter
,它具有以下属性(为了支持依赖项注入):

这允许我编写任意数量的特定于语言的解析器和格式化程序。例如,我有一个
CSharpParser
和一个
JavascriptParser
。我还有一个
HtmlCodeFormatter
,还有另一个计划(功能可疑)

其思想是可以使用对象初始值设定项实例化
HtmlFormatter
,如下所示:

public class Foo
{
    public Bar RequiredProperty { get; set;}

    public void Baz()
    {
        if (this.RequiredProperty == null)
        {
            // Which exception should I throw?
        }
    }
}
var formatter = new HtmlCodeFormatter()
    {
        Parser = new CSharpParser();
        Formatter = new HtmlCodeFormatter();
    };
formatter.Format("Console.WriteLine(\"Hello, world!\"));
调用
HtmlCodeFormatter.Format
时,它需要能够验证是否同时提供了解析器和格式化程序。这真的没问题,但我有点困惑于该抛出哪个异常。我倾向于
invalidooperationexception
,但我不完全确定这是最佳选择。

我会抛出一个错误。MSDN的定义是:

异常发生时引发的异常 方法调用对于的无效 对象的当前状态


但是,我建议使用构造函数注入而不是setter注入。这将确保您拥有有效的解析器和格式化程序。如果在构造函数中传递null,则抛出一个。

您可以通过使用属性注入来抛出
NotInitializedException

,这是在隐式地告诉编译器、其他开发人员以及DI容器依赖项是可选的。这是因为没有人强迫您指定属性值

如果确实需要依赖项,则应重构为构造函数注入。这将有效地使依赖性成为必需的:

public class Foo
{
    private readonly Bar bar;

    public Foo(Bar bar)
    {
        if (bar == null)
        {
            throw new ArgumentNullException("bar");
        }

        this.bar = bar;
    }

    public Bar RequiredProperty
    {
        get { return this.bar; }
    }

    public void Baz()
    {
        // this.bar is now GUARANTEED to be initialized,
        // so no checks are required
    }
}
请注意,构造函数保护子句(抛出ArgumentNullException)有效地保护了类的不变量,而
readonly
关键字进一步加强了这一点


使用类不变量的依赖部分,您不再具有任何时间耦合,现在可以实现所有成员,而无需担心依赖项是否已分配。

为什么不在构造函数中传递它们?是否有那么多属性使得传递给构造函数变得难以读取?@CodeInChaos:好问题。在这种情况下,这是品味的问题。就我个人而言,我发现对象初始值设定项更具可读性。C#4中的命名参数可以增加很多可读性。Jon Skeet同意这一点。我被出卖了。(见:)。那就来吧,太好了!我完全明白你的意思,但我的印象是这是一个非常糟糕的主意™ 在构造函数中抛出异常,并且不惜一切代价避免它™.一点也不。这一原理被称为快速失效。你从哪里得到这个主意的?你知道,我真的不知道。我很可能会遇到TypeInitializationFailed异常,这可能会掩盖真正的问题。但我是fail fast的粉丝,所以我在您的指导下修复了我的代码。什么名称空间不是InitializedException?