【问题标题】: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@中得到了你想要的东西

标签: c# .net nullable null-coalescing-operator


【解决方案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 合并运算符的左右表达式类型是不同的类型。
    • 我是这么认为的,因此是“可能”。我删除了那部分答案。
    • 我更喜欢反转检查值,如果没有值则抛出。听起来更合乎逻辑。
    猜你喜欢
    • 1970-01-01
    • 2011-10-11
    • 2019-10-19
    • 1970-01-01
    • 1970-01-01
    • 2016-08-20
    • 2022-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多