是否可以使用运算符?并抛出新的异常()?

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&lt;T&gt; 方法,一个用于值类型/结构,一个用于引用类型?前者将使用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:我真的不认为这值得投反对票!问题中的示例代码抛出一个普通的ExceptionInvalidCastExceptionNullReferenceException 比没有额外细节的普通 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 合并运算符的左右表达式类型是不同的类型。 我是这么认为的,因此是“可能”。我删除了那部分答案。 我更喜欢反转检查值,如果没有值则抛出。听起来更合乎逻辑。

以上是关于是否可以使用运算符?并抛出新的异常()?的主要内容,如果未能解决你的问题,请参考以下文章

异常机制

C++ 隐式生成的赋值运算符的异常安全性

怎样自定义一个异常(报错) 并抛出

Object类和异常

字符串连接运算符(||)在配置单元中抛出错误

如何解决'定义并抛出专用异常而不是使用通用异常'。