【问题标题】:CLR: If a constructor fails will it always throw an exception?CLR:如果构造函数失败,它总是会抛出异常吗?
【发布时间】:2009-06-28 12:31:23
【问题描述】:
在Delphi中,如果在构建对象的过程中出现异常:任何分配的内存都会被释放并抛出异常。例如,保证返回一个有效的Camera对象,或者抛出一个异常:
Camera c = new Camera();
您从不必须检查结果变量是否为空:
Camera c = new Camera();
if (c == null)
throw new Exception("Error constructing Camera") //waste of time
在 CLR 中也是如此吗?
还有其他语法结构可以保证返回值有效或抛出异常吗?
- 创建结构(例如矩形)?
- 获取枚举成员?
- Object.ToString() 的结果?
- 数学运算?
在进行数学运算的情况下:
Int32 aspect = 1650.0 / 1080.0;
if (aspect == null)
throw new Exception("Division of two numbers returned null")
【问题讨论】:
标签:
.net
clr
defensive-programming
【解决方案1】:
.Net 中的构造函数保证返回对象类型的非空实例。实例是否有效取决于类型的各个语义。
构造函数中抛出的异常不会被 CLR 任意吞下(尽管用户代码可以吞下它们)。 CLR 将像在任何其他方法中引发的异常一样传播异常,并且最终将正确地对对象进行垃圾收集。
至于你提到的其他情况
- 创建结构:根据定义,结构永远不能为空。构造函数中抛出的异常会正常传播
- 获取枚举成员:枚举是底层的结构/值类型,也永远不会为空
- Object.ToString() 的结果:这可以(很遗憾地)为空。 String 是一种引用类型,从 ToString 覆盖返回 null 是完全合法的(请不要)。
- 数学运算:这在很大程度上取决于项目的溢出设置和所使用的特定类型(整数与浮点)。
这个数学问题几乎值得自己回答。一方面,对原始类型进行数学运算的结果永远不会为空。但它仍然可能无效。例如,以下代码不会抛出,但结果是否有效在很大程度上取决于您的具体场景
float f1 = 1.0;
float f2 = f1 / 0;
此时 f2 是一个非常具体的浮点值,不代表实数。它有效吗?取决于您的用例。
【解决方案2】:
是的。我想这样说(因为 fail 也可能意味着 logical failure):如果构造函数没有抛出异常,则返回值保证为non-null 所以你永远不必执行这样的检查。
创建结构(例如矩形):struct 根本不能是null(Nullable 类型被认为是完全不同的类型,即typeof(int?) != typeof(int))。调用结构的构造函数要么会因抛出异常而失败,要么会返回一个实例。
获取枚举成员:enum 只是一组常量。没有什么能比得上“在运行时获取成员”。它在编译时被替换。
Object.ToString() 的结果:与任何方法一样,它可以返回 string 类型的任何有效值,包括 null,并且还可以抛出异常(在这种情况下,它根本不返回值)。
数学运算:所有表达式都会返回值或抛出异常。返回值可以是该类型的任何有效值(例如,Int32 永远不能是 null)。