有没有办法将 "throw new Exception()" 挤进一个对象?
Is there a way to squeeze "throw new Exception()" into an object?
一些其他高级语言,如 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
}
有什么规范的方法可以做到这一点,或者有什么好的理由不这样做吗?
编辑:
我实际上并没有尝试使用 throw new Exception()
作为 C# 7 中的一个值,直到发布这个。这就是答案,或多或少。我会保留它,以防将来人们搜索 C# 相当于 Perl 6 的 Failure
class 或 Haskell 的 error
.
C# 7.0 支持 throw
expressions:
return mode == BuildMode.Debug ? true
: mode == BuildMode.MemoryProfiling ? true
: mode == BuildMode.Release ? false
: throw new Exception("Unhandled mode: " + mode);
没有惰性求值,但您不再需要辅助方法。
我怀疑您正在寻找在 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");
懒惰评价
对于惰性求值,您必须 return 一个 函数 或一个 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,它也是 return 具有更方便语法的 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#
给定的答案是正确的,但我将添加一个答案(针对我自己的问题)以指出在 C# 6 及更低版本中模拟 throw 表达式的理想方法。具有相同的名称和类似的 API 对于向前兼容性很有用,所以这是我选择的助手 class:
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);
}
}
也可以制作一个惰性抛出表达式,它只会在转换为目标值类型时抛出。根据您的判断是否使用此 class 的方式会使代码的类型安全性降低(从 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
}
}
可以进行各种修饰,例如接受不同的异常类型作为参数或通过额外的模板参数,但我打算在需要这些增强功能之前保持简单。
一些其他高级语言,如 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
}
有什么规范的方法可以做到这一点,或者有什么好的理由不这样做吗?
编辑:
我实际上并没有尝试使用 throw new Exception()
作为 C# 7 中的一个值,直到发布这个。这就是答案,或多或少。我会保留它,以防将来人们搜索 C# 相当于 Perl 6 的 Failure
class 或 Haskell 的 error
.
C# 7.0 支持 throw
expressions:
return mode == BuildMode.Debug ? true
: mode == BuildMode.MemoryProfiling ? true
: mode == BuildMode.Release ? false
: throw new Exception("Unhandled mode: " + mode);
没有惰性求值,但您不再需要辅助方法。
我怀疑您正在寻找在 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");
懒惰评价
对于惰性求值,您必须 return 一个 函数 或一个 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,它也是 return 具有更方便语法的 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#
给定的答案是正确的,但我将添加一个答案(针对我自己的问题)以指出在 C# 6 及更低版本中模拟 throw 表达式的理想方法。具有相同的名称和类似的 API 对于向前兼容性很有用,所以这是我选择的助手 class:
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);
}
}
也可以制作一个惰性抛出表达式,它只会在转换为目标值类型时抛出。根据您的判断是否使用此 class 的方式会使代码的类型安全性降低(从 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
}
}
可以进行各种修饰,例如接受不同的异常类型作为参数或通过额外的模板参数,但我打算在需要这些增强功能之前保持简单。