Haskell和Perl 6等其他一些高级语言提供了语法糖,即使在语法要求对象的地方,该糖也允许引发异常。当使用该值时,它的行为就好像它变成了引发的异常(在以下非常人为的示例中将立即发生):
enum BuildMode { Debug, MemoryProfiling, Release };
bool IsDebugMode(BuildMode mode)
{
return mode == BuildMode.Debug ? true
: mode == BuildMode.MemoryProfiling ? true
: mode == BuildMode.Release ? false
: ThrowException<bool>("Unhandled mode: " + mode);
}
以上帮助器允许从允许值而不是语句的位置引发异常。我可以按如下方式编写此函数,尽管它没有Haskell或Perl 6代码那么酷,因为它没有惰性的评估:
T ThrowException<T>(string message)
{
#line hidden
throw new Exception(message);
#line default
}
是否有任何规范的方法可以做到这一点,或者有充分的理由不这样做?
编辑:
在发布此代码之前,我实际上并没有尝试在C#7中使用throw new Exception()
作为值。或多或少,这就是答案。如果将来有人搜索与Perl 6的Failure
类或Haskell的error
类等效的C#,我将不做介绍。
答案 0 :(得分:6)
C#7.0支持throw
expressions:
return mode == BuildMode.Debug ? true
: mode == BuildMode.MemoryProfiling ? true
: mode == BuildMode.Release ? false
: throw new Exception("Unhandled mode: " + mode);
没有偷懒的评估方法,但是您不再需要辅助方法。
答案 1 :(得分:3)
我怀疑您正在寻找C#7中添加的throw expressions。
return mode == BuildMode.Debug ? true
: mode == BuildMode.MemoryProfiling ? true
: mode == BuildMode.Release ? false
: throw new Exception(...);
最常见的用法之一是用于空参数验证
var nameValue = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "New name must not be null");
惰性评估
对于惰性计算,您必须返回 function 或Lazy:
Lazy<bool> IsDebugMode(BuildMode mode)
{
bool isDebug()
{
return mode == BuildMode.Debug ? true
: mode == BuildMode.MemoryProfiling ? true
: mode == BuildMode.Release ? false
: throw new Exception(...);
}
return new Lazy<bool>(isDebug);
}
并将其用作:
var isDbg=IsDebugMode(someMode);
.....
.....
//Will throw here!
if (isDbg.Value)
{
...
}
F#提供了lazy computations,它也以更方便的语法返回了Lazy:
let isDebugMode mode =
match mode with
| BuildMode.Debug -> true
| BuildMode.Release -> false
| _ -> failwith "Ouch!"
let isDbg = lazy (isDebugMode someMode)
...
//Can throw here
if (isDbg.Force() then
...
使用Func进行相同的惰性评估:
Func<bool> IsDebugMode(BuildMode mode)
{
bool isDebug()
{
return mode == BuildMode.Debug ? true
: mode == BuildMode.MemoryProfiling ? true
: mode == BuildMode.Release ? false
: throw new Exception(...);
}
return isDebug;
}
用作功能:
var isDbg=IsDebugMode(someMode);
...
//Can throw here
if(isDbg())
{
...
}
切换表达式
C#8将添加可能 如下的开关表达式:
return mode switch {
BuildMode.Debug => true,
BuildMode.MemoryProfiling => true,
BuildMode.Release => false,
_ => throw new Exception (...)
};
惰性函数可能如下所示:
Lazy<bool> IsDebugMode(BuildMode mode)
{
bool isDebug() =>
mode switch {
BuildMode.Debug => true,
BuildMode.MemoryProfiling => true,
BuildMode.Release => false,
_ => throw new Exception (...)
};
return new Lazy<bool>(isDebug);
}
看起来更像F#
答案 2 :(得分:0)
给出的答案是正确的,但是我将添加一个答案(针对我自己的问题),以指出一种在C#6及更低版本中模拟throw表达式的理想方法。具有相同的名称和相似的API对于前向兼容性很有用,因此这是我确定的帮助程序类:
public class ThrowExpression<T>
{
public ThrowExpression(string message)
{
#line hidden
throw new Exception(message);
#line default
}
// never used, but makes the compiler happy:
public static implicit operator T(ThrowExpression<T> obj)
{
return default(T);
}
}
还可以创建一个惰性抛出表达式,该表达式仅在将其强制转换为目标值类型时才会抛出。对于是否以减少代码类型安全性的方式使用此类(从object
到目标类型进行广播),请做出判断。
public class ThrowExpression<T>
{
private string message;
public ThrowExpression(string message)
{
this.message = message;
}
public static implicit operator T(ThrowExpression<T> obj)
{
#line hidden
throw new Exception(message);
#line default
}
}
各种修饰都是可能的,例如接受不同的异常类型作为参数或通过附加的模板参数,但是我打算保持简单,直到需要这些增强。