我有一个看起来像这样的方法:
void throwException(string msg)
{
throw new MyException(msg);
}
现在,如果我写
int foo(int x, y)
{
if (y == 0)
throwException("Doh!");
else
return x/y;
}
编译器会抱怨foo“并非所有路径都返回一个值”。
是否有一个属性我可以添加到throwException以避免这种情况?类似的东西:
[NeverReturns]
void throwException(string msg)
{
throw new MyException(msg);
}
我担心自定义属性不起作用,因为为了我的目的,我需要编译器的合作。
答案 0 :(得分:31)
为什么不将其改为
int foo(int x, y)
{
if (y == 0)
throwException("Doh!");
return x/y;
}
这给出了相同的运行时结果,编译器不会抱怨。
答案 1 :(得分:22)
没有。我建议你更改你的第一个函数的签名以返回异常而不是抛出它,并将throw语句保留在你的第二个函数中。这将使编译器保持愉快,并且闻起来也不那么糟糕。
答案 2 :(得分:8)
Bernhof的回答是正确的。但是,如果您在实例化异常时尝试封装大量逻辑,那么您需要做的就是更改代码:
void throwException(string msg) {
throw new MyException(msg);
}
到此:
Exception makeException(string msg) {
return new MyException(msg);
}
然后您的调用代码将如下所示:
int foo(int x, y) {
if (y == 0) {
throw makeException("Doh!");
}
return x / y;
}
所有其他条件相同,更喜欢功能代码到程序代码。它更容易重复使用和单元测试。
修改强>
根据Fred的示例代码,这就是我要做的。这不是代码合同,但它仍然有效。
private int getVarID(string s_varID) {
int varID;
if(s_varID == "ILT") {
return 123;
} else if(s_varID == "TL") {
return 456;
} else if(s_varID == "FT") {
return 789;
} else if(int.TryParse(s_varID, out varID)) {
return varID;
} else {
throw makeParseError("varID must be an integer or 'ILT', 'TL' or 'FT'.");
}
}
答案 3 :(得分:5)
您可以使用泛型声明函数返回“任何内容”来指示“永不返回”:
T ThrowException<T>(string msg)
{
throw new MyException(msg);
}
现在你可以写:
int foo(int x, int y)
{
if (y == 0)
return ThrowException<int>("Doh!");
else
return x/y;
}
此成语用于Haskell和F#等语言,基于principle of explosion,也称为“ex falso quodlibet”。原因是:如果一个函数永远不会返回,那么我们可以对它的返回值做出我们想要的任何神奇假设,因为这样的值永远不会存在。这里,调用者(foo)假定ThrowException将返回一个int。
一些小缺点:
ThrowException
的实施可以通过返回default(T)
来解决这个问题。ThrowException
时必须指定返回类型(Haskell和F#可以推断出来)。正如其他答案所说的那样,你可能最好不要抛弃异常而不是扔掉它。
答案 4 :(得分:3)
不要将异常创建移交给另一个函数(即直接抛出),编译器不会抱怨。将异常抛出的“帮助”类型函数移交给浪费时间是浪费时间,除非该函数实际上是将值添加到异常处理中。
答案 5 :(得分:3)
[DoesNotReturn]
中的System.Diagnostics.CodeAnalysis
属性应该是您想要的。
答案 6 :(得分:2)
你的功能
void throwException(string msg)
{
throw new MyException(msg);
}
为代码添加零值,因此您的问题没有实际意义。另一方面,如果你想在整个类中使用相同的消息抛出错误并最小化代码重复,那么这就是你应该做的。
通常的做法是为这个特殊情况延长MyException
并抛出:
public class HomerSimpsonException : MyException
{
public HomerSimpsonException() : base ("DOH!!!"){
}
}
int foo(int x, y)
{
if (y == 0)
throw new HomerSimpsonException();
else
return x/y;
}
即便如此,根据Microsoft规则扩展异常,这还不够完整,您应该实现最少4个构造函数 - http://msdn.microsoft.com/en-us/library/ms182151%28VS.80%29.aspx,即:
public NewException(){}
public NewException(string){}
public NewException(string, Exception){}
protected or private NewException(SerializationInfo, StreamingContext){}
答案 7 :(得分:1)
Bernhof已经给你一个避免编译器抱怨的方法。但是,还要注意您的堆栈跟踪将关闭(并且一些记录器库将不会处理util-classed-i-throw-exceptions-for-your-app方法),这使得调试应用程序变得更加困难。
答案 8 :(得分:1)
删除'else'关键字,无论如何都是多余的,它会起作用;)
答案 9 :(得分:1)
嗯,这是一个“非常”低效的实施。
工人阶级:
/// <summary>
/// Representation of an unreachable type, exposing a method to represent unreachable code.
/// </summary>
public static class Unreachable {
/// <summary>
/// Representation of unreachable code with return semantics.
/// </summary>
public static dynamic Code() {
throw new NotImplementedException(@"Unreachable code was reached.");
}
}
示例:
public object[] UnreachableCodeTest() {
return Unreachable.Code();
}
反编译:
Offset OpCode Operand
0 ldsfld System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
5 brtrue.s -> (10) ldsfld System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
7 ldc.i4.0
8 ldtoken System.Object[]
13 call System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)
18 ldtoken TestApp.Program
23 call System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)
28 call System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder::Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,System.Type,System.Type)
33 call System.Runtime.CompilerServices.CallSite`1<!0> System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>>::Create(System.Runtime.CompilerServices.CallSiteBinder)
38 stsfld System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
43 ldsfld System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
48 ldfld !0 System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>>::Target
53 ldsfld System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
58 call System.Object TestApp.Unreachable::Code()
63 callvirt !2 System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>::Invoke(!0,!1)
68 ret
通过实施Wody Fody,搜索此呼叫签名并将其替换为单个原始ret(如果它可以让您)或其他一些简单可接受的低成本替代方案进行优化。
(编辑)
当你模仿我时,我将被迫解释为什么这是有效和有用的。
大多数情况下,这会进入一个类似于具有逻辑上不可达路径的方法结尾的块;
#pragma warning disable 162
// ReSharper disable CSharpWarnings::CS0162
// ReSharper disable HeuristicUnreachableCode
return Unreachable.Code();
// ReSharper restore HeuristicUnreachableCode
// ReSharper restore CSharpWarnings::CS0162
#pragma warning restore 162
如果以允许访问代码的方式修改本节上面的代码,则测试将失败。 如果您有下面的代码,那么您错了,编译器会通知您。 一旦超出初始成熟度,您将一起删除此代码块。 这样做的主要目的是捕获代码不时无法访问的情况。
在代码不应该到达但编译器无法逻辑排除的其他情况下(例如,在案例中为默认语句),您通常会抛出错误。如果要针对此方案进行优化,则需要此类实现。
select( thisIsAlways123Never0OrGreaterThan3 ) {
default: return Unreachable.Code();
case 1: DoSomething(); break;
case 2: DoSomethingElse(); break;
case 3: return GetSomething();
}
要优化使用此最低编写代码为default:
路径发出最小指令,您需要一位名为Fody的朋友。
理想情况是类似于C ++开发人员在GCC和GCC中所希望的结果。空方法上的LLVM __builtin_unreachable()
或MSVC __assume(0)
或__declspec(noreturn)
。