是否可以使用运算符?并抛出新的异常()?
Posted
技术标签:
【中文标题】是否可以使用运算符?并抛出新的异常()?【英文标题】:Is it possible to use operator ?? and throw new Exception()? 【发布时间】:2010-12-18 06:42:25 【问题描述】:接下来我有很多方法要做:
var result = command.ExecuteScalar() as Int32?;
if(result.HasValue)
return result.Value;
else
throw new Exception(); // just an example, in my code I throw my own exception
我希望我可以像这样使用运算符??
:
return command.ExecuteScalar() as Int32? ?? throw new Exception();
但它会产生编译错误。
是否可以重写我的代码或者只有一种方法可以做到这一点?
【问题讨论】:
我希望能够return this as T ?? that as T ?? other as T ?? throw new NotSupportedException();
相反,我必须使用临时变量,测试空值,然后返回临时变量。就是丑了点。
在 Connect() 2016 上有一个演示文稿展示了即将推出的 C# 7 的此功能。
看来你在 C#7 structuredsight.com/2016/09/01/c-7-additions-throw-expressions987654321@中得到了你想要的东西
【参考方案1】:
对于 C# 7
在 C# 7 中,throw
变成了一个表达式,因此完全可以使用问题中描述的代码。
适用于 C# 6 及更早版本
你不能在 C# 6 和更早版本中 直接 这样做 - ?? 的第二个操作数必须是表达式,而不是 throw 语句。
如果您真的只是想找到一个简洁的选项,还有一些选择:
你可以写:
public static T ThrowException<T>()
throw new Exception(); // Could pass this in
然后:
return command.ExecuteScalar() as int? ?? ThrowException<int?>();
我真的不建议你这样做......这很可怕而且很不习惯。
扩展方法怎么样:
public static T ThrowIfNull(this T value)
if (value == null)
throw new Exception(); // Use a better exception of course
return value;
然后:
return (command.ExecuteScalar() as int?).ThrowIfNull();
另一种选择(同样是一种扩展方法):
public static T? CastOrThrow<T>(this object x)
where T : struct
T? ret = x as T?;
if (ret == null)
throw new Exception(); // Again, get a better exception
return ret;
致电:
return command.ExecuteScalar().CastOrThrow<int>();
这有点难看,因为您不能将 int?
指定为类型参数...
【讨论】:
我想这是因为你没有以 Tony 的身份回答。无论如何,为你反驳它。您在这里走在正确的轨道上,但认为有一种更好、更通用的技术,我将添加作为我自己的回应(冒着被否决的风险) Jon,您能否使用泛型参数约束来创建两个CastOrThrow<T>
方法,一个用于值类型/结构,一个用于引用类型?前者将使用T?
,而后者将使用T
。
@Adam:不幸的是,你不能有两种方法,它们的签名的唯一区别是输出类型和/或通用约束。
扩展方法!简直太棒了ThrowIfNull
+1
嗨@JonSkeet,您可能想要更新此答案,并提示(在 C#7 中)添加的执行 OP 想要的功能:structuredsight.com/2016/09/01/c-7-additions-throw-expressions【参考方案2】:
如前所述,您不能使用 ??运算符(嗯,并非没有一些扭曲,似乎不符合您使这个更清洁的目标)。
当我看到这种模式出现时,我立即想到了Enforcements。最初来自 C++ 世界,它们很好地转移到了 C#,尽管可以说大部分时间不那么重要。
这个想法是你采取某种形式:
if( condition )
throw Exception;
并将其转换为:
Enforce<Exception>( condition );
(您可以通过默认异常类型来进一步简化)。
更进一步,您可以为不同的条件检查编写一组 Nunit 风格的方法,例如;
Enforce<Exception>.NotNull( obj );
Enforce<Exception>.Equal( actual, expected );
Enforce<Exception>.NotEqual( actual, expected );
等等
或者,更好的是提供一个期望的lamba:
Enforce<Exception>( actual, expectation );
真正巧妙的是,一旦你这样做了,你可以返回 actual 参数并强制执行 inline:
return Enforce( command.ExecuteScalar() as Int32?, (o) => o.HasValue ).Value;
...这似乎是最接近你所追求的。
我之前已经完成了一个实现。有几个小问题,比如你如何一般地创建一个接受参数的异常对象——那里有一些选择(我当时选择了反射,但将工厂作为额外参数传入可能会更好)。但总的来说,这一切都非常简单,可以真正清理大量代码。
在我的清单上,要完成一个开源实现。
【讨论】:
这里有什么不使用扩展方法的理由吗? “return (command.ExecuteScalar() as int?).Enforce(x => x.HasValue);”对我来说读起来稍微好一些……尽管在那时更改名称可能值得。我确实喜欢使用谓词的想法。 主要是因为当我第一次在 C# 中执行此操作时,我使用的是 C#2 ;-) 出于同样的原因,我最初并没有将 lambda 用于谓词,但这是不费吹灰之力的到。我认为扩展方法可以很好地工作,但必须稍微尝试一下。【参考方案3】:如果您只想在返回值不是 Int32
时出现异常,请执行以下操作:
return (int)command.ExecuteScalar();
如果您想抛出自己的自定义异常,那么我可能会这样做:
int? result = command.ExecuteScalar() as int?;
if (result == null) throw new YourCustomException();
return result.Value;
【讨论】:
是的,会抛出异常。但也许无效的强制转换异常不是要抛出的适当异常;如果该命令没有返回值,则可能需要特定于应用程序的异常。 @Adam:我真的不认为这值得投反对票!问题中的示例代码抛出一个普通的Exception
。 InvalidCastException
或 NullReferenceException
比没有额外细节的普通 Exception
更适合且信息丰富。
让我换个说法;如果命令没有返回值,则可能需要抛出特定于应用程序的异常(例如,NoRecordsExistException
)(是的,我知道发帖人没有提到这样一件事,但有些人确实从他们的问题中删除了这些特殊性。)为此,必须将您发布的声明包装在 try
/catch
块中,这会破坏压缩代码的目的。
@Adam:确实如此,但如果没有来自 OP 的进一步信息,我会重申我的代码比问题中的代码更简单,并引发 异常更多信息/适当。如果这不是 OP 要求的,那么他们应该在问题中澄清。
我喜欢这个。怎么样:尝试 return (int)command.ExecuteScalar(); 捕捉 抛出新的 NotFoundException(); 【参考方案4】:
您将无法在 null 合并运算符的右侧引发异常。这背后的原因是运算符的右侧需要是表达式,而不是语句。
null 合并操作符是这样工作的:如果操作符的左值为 null,则返回它;否则,返回运算符右侧的内容。 throw
关键字不返回值;因此,它不能用于运算符的右侧。
【讨论】:
【参考方案5】:你做不到的原因:
return command.ExecuteScalar() as Int32? ?? throw new Exception();
是因为抛出异常是语句,而不是表达式。
如果您只是想稍微缩短代码,也许是这样:
var result = command.ExecuteScalar() as Int32?;
if(result.HasValue) return result;
throw new Exception();
不需要其他的。
【讨论】:
这只有在这个 return 语句所在的函数返回一个 Object 时才有效。任何其他返回类型都会导致编译器错误,因为 null 合并运算符的左右表达式类型是不同的类型。 我是这么认为的,因此是“可能”。我删除了那部分答案。 我更喜欢反转检查值,如果没有值则抛出。听起来更合乎逻辑。以上是关于是否可以使用运算符?并抛出新的异常()?的主要内容,如果未能解决你的问题,请参考以下文章