C#中是否存在“ true或throw”或“ non-null或throw”表达式?

时间:2019-06-15 19:52:14

标签: c# expression throw

给出以下工作代码:

if (!this.TryParse()) throw new Exception();
// or
if (this.Parse() == null) throw new Exception();

由于C#7已经使用???:知道了 throw表达式,所以我想知道是否(或为什么不这样)将其写为“ C#中的true或throw”或“ non-null或throw”语句,类似于JavaScript或PHP等其他语言所允许的内容。这是我尝试过的:

// COMPILE ERROR:

this.TryParse() || throw new Exception();

this.Parse() ?? throw new Exception();

_ = this.TryParse() || throw new Exception();


// WORKS:

_ = this.TryParse() ? 0 : throw new Exception();

_ = this.Parse() ?? throw new Exception();

尤其是为什么_ = <obj> ?? <throw>可以工作但是_ = <bool> || <throw>不能工作?

我为什么要这样做?纯粹的可读性:我想使主要目的是执行而不是测试的方法作为调用语句在行的开头清晰可见,而不是将它们隐藏在if条件中。对比一下PHP:

mysql_connect(...)
    OR die();

4 个答案:

答案 0 :(得分:2)

在这种情况下可以使用扩展方法...

static void Main(string[] args)
{
    var a = false;

    a.OrThrow();
}


}
public static class ThrowExtension {
    public static void OrThrow(this bool b)
    {
        if(!b)
            throw new Exception();
    }
}

答案 1 :(得分:2)

显而易见的答案是“因为他们没有那样指定”。可以在here中找到有关throw表达式的摘要。我认为我们只能猜测原因,但实际上我认为这是有道理的。所有新的语言功能似乎都符合这一目的:能够以更实用的方式编写代码。让我们看一下您的解析示例:

namespace App\Http\Controllers\Auth;

use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
use Auth;

class RegisterController extends Controller
{
    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    //public $redirectTo = '/home';
    protected function redirectPath()
    {
        $role = Auth::user()->role;
        if ($role === 'candidate') {
            return '/candidate_dashboard';
        } elseif ($role === 'employer') {
            return '/employer_dashboard';
        } elseif ($role === 'contractor') {
            return '/contractor_dashboard';
        }
    }

    public function __construct()
    {
        $this->middleware('guest');
    }

    protected function validator(array $data)
    {
        try {
            return Validator::make($data, [
                'first_name' => ['required', 'string', 'max:255'],
                'last_name' => ['required', 'string', 'max:255'],
                'role' => ['required', 'string', 'max:50'],
                'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
                'password' => ['required', 'string', 'min:8', 'confirmed'],
            ]);
        } catch (Illuminate\Database\QueryException  $th) {
            return back()->withError($th->getMessage())->withInput();
        }
    }

    protected function create(Request $data)
    {
        try {
            return User::create([
                'first_name' => $data['first_name'],
                'last_name' => $data['last_name'],
                'role' => $data['role'],
                'email' => $data['email'],
                'password' => Hash::make($data['password']),
            ]);
        } catch (Illuminate\Database\QueryException $th) {
            return back()->withError($th->getMessage())->withInput();
        }
    }
}

要在不使用throw表达式的情况下编写此代码,您必须对var parsed = Parse() ?? throw new Exception(); 进行两次评估,或者出于一个目的而两次写下Parse变量。

parsed

if (Parse() == null) throw new Exception();
var parsed = Parse();

因此,前两个示例很简单,在各个方面写var parsed = Parse(); // variable `parsed` can be null at this point if (parsed == null) throw new Exception(); // variable `parsed` cannot be null anymore, the type of the variable is essentially mutated 而不是this.TryParse() || throw new Exception();实际上是相同的。那么,为什么要为我们已经拥有的相同概念引入一种新的语法呢?记住Python的语言设计原则:

if (!this.TryParse()) throw new Exception();

最后一个例子比较棘手。语句There should be one-- and preferably only one --obvious way to do it. 在语法上与var a_non_false = this.TryParse() || throw new Exception();非常相似,但是我认为这有很大的不同。变量var a_non_null = this.Parse() ?? throw new Exception();本质上只是一个a_non_false常量(好像是假的,我们已经抛出了异常),因此我们可以在其余的内容中将true替换为a_non_false该功能,并获得相同的行为。

对于true部分,我认为这是一个不同的问题。我想知道为什么他们不禁止在如此简单的分配中使用discard variables。我绝对会避免使用它们来代替经典的流控制。

因此,总而言之,起初这些设计决策看起来是武断的,但经过一秒钟的思考,它似乎经过深思熟虑。

答案 2 :(得分:1)

_ = <obj> ?? <throw>之所以有效,是因为如果对象为null,则抛出异常而不是返回值。 ??右侧的表达式的类型无关紧要。

_ = <bool> || <throw>不起作用,因为||仅可应用于两个布尔值。右侧的表达式不是布尔值。 (我会说,它是未定义的。)

答案 3 :(得分:1)

您为什么要使用这种语法?代码应尽可能可读,因为尽管编写一次,但通常可能被读取数十次。如果我看到以下情况之一:

if (!this.TryParse()) throw new Exception();
// or
if (this.Parse() == null) throw new Exception();

我自动假定程序员感到困惑。为什么?因为两件事:

  1. 一个TryParse函数返回一个布尔值并填充输出参数。当您完全可以确定输入值不可解析但不希望发生异常时使用它。示例:int.TryParse()

  2. 一个Parse函数假定输入有效,并返回有效的非null值,或者引发异常。在这种情况下,抛出新异常是多余的,并且可能使实际问题难以理解。示例:int.Parse()

请记住,该示例可能是一个糟糕的示例,让我们看几个替代方案。首先,您可以为此编写扩展方法:

public static AssertNotNull(this object item) {
  if (item == null)
    throw new ArgumentException(“Object was null.”);
}

因此,我们有两种替代语法:

  1. 您的语法(_ = this.TryParse() ? 0 : throw new Exception();
  2. 我的语法:
    var val = this.TryParse();
    val.AssertNotNull();

两者中哪个更容易阅读?最近,当他使用您的语法时,我对同事的同行评价发表了评论。在他的情况下,如果三元组的第一部分不为空,则他将在三元组的右侧执行任务。不好。

底线:人们应该始终尽可能明确地编写代码。