web-dev-qa-db-ja.com

式本体のメンバーから例外をスローできないのはなぜですか?

式本体のメンバーを使用すると、メソッドまたはプロパティの本体を、returnキーワードなしで単一の式として定義できます(何かを返す必要があります)。

例えばそれはこれらを回します

int Method1()
{
    return 5;
}

void Method2()
{
    Console.WriteLine();
}

これらに

int Method1() => 5;

void Method2() => Console.WriteLine();

ボディから例外をスローすると、違いが生じます。

void Method3()
{
    throw new Exception();
}

ただし、以下はコンパイルされません。

void Method3() => throw new Exception();

次のメッセージが表示されます。

Warning The member 'Program.Exception()' does not hide an inherited member. The new keyword is not required.  
Error   'Program.Exception()' must declare a body because it is not marked abstract, extern, or partial  
Error   ; expected  
Error   Invalid token 'throw' in class, struct, or interface member declaration  
Error   Method must have a return type
Error   Invalid expression term 'throw' 

どうして?

29
Jeroen Vannevel

これは、最初の2つのコードスニペット(_5_と_Console.WriteLine_)が式であるために発生します。より具体的には、これらはそれぞれNumericLiteralExpressionInvocationExpressionです。

後者(throw new Exception())はステートメントです-この場合:ThrowStatement

Roslyn SDKを見ると、MethodDeclarationSyntaxオブジェクトにタイプExpressionBodyのプロパティArrowExpressionClauseSyntaxがあり、タイプExpressionSyntaxのプロパティがあることがわかります。これにより、式本体のメンバーでは式のみが受け入れられることが明らかになります。

最後のコードサンプルを見ると、ThrowStatementSyntaxで構成されており、ExpressionSyntaxプロパティが含まれていることがわかります。この例では、それをObjectCreationExpressionSyntaxオブジェクトで埋めています。


式とステートメントの違いは何ですか?

なぜそれはステートメントも受け入れないのですか?

私はここでしか推測できませんが、それは例外をスローすることができないほど多くの副作用を開くためだと思います。式とステートメントが継承に共通の祖先を持っているとは思わないので、コードの重複がたくさんあります。結局のところ、それはある意味で理にかなっているとしても、単に面倒になる価値がないということになると思います。

単純な式をメソッド本体の一部として記述し、実際にはExpressionStatementSyntaxでラップする場合、両方を組み合わせます。これにより、メソッドのBodyプロパティの下で他のステートメントと一緒にグループ化できます。ボンネットの下で、彼らはこれを展開し、そこから表現を抽出しているに違いありません。この時点では、式だけが残り、ステートメントはなくなっているため、これを式本体のメンバーに使用できます。

ただし、ここで重要な注意点の1つは、returnステートメントが..ステートメントであるという事実です。より具体的には、ReturnStatementSyntax。彼らはこれを明示的に処理し、コンパイラの魔法を適用したに違いありませんが、それは疑問を投げかけます:なぜThrowStatementSyntaxに対して同じことをしないのですか?

次のシナリオを考えてみましょう。突然、throwステートメントも受け入れられます。ただし、式本体のメンバーは、その本体(duh)として式のみを持つことができるため、throwキーワードを省略し、代わりにnew Exception()を残す必要があります。 returnステートメントとthrowステートメントの意図をどのように区別しますか?

これら2つの方法の表現本体のバリエーションに違いはありません。

_public Exception MyMethod()
{
    return new Exception();
}

public Exception MyMethod()
{
    throw new Exception();
}
_

throwステートメントとreturnステートメントはどちらも有効なメソッド終了です。ただし、それらを省略すると、2つを区別するものは何もありません--ergo:新しく作成された例外オブジェクトを返すかスローするかはわかりません。

これから何を取り除くべきですか?

式本体のメンバーは、その名前が示すとおり、本体に式のみを持つメンバーです。これは、式を正確に構成するものを認識している必要があることを意味します。それが一つの「ステートメント」であるからといって、それを表現にするわけではありません。

35
Jeroen Vannevel

この機能はC#7で提供されます。から https://blogs.msdn.Microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/

式の途中で例外をスローするのは簡単です。それを実行するメソッドを呼び出すだけです。ただし、C#7.0では、特定の場所で式としてthrowを直接許可しています。

class Person
{
    public string Name { get; }
    public Person(string name) => Name = name ?? throw new ArgumentNullException(name);
    public string GetFirstName()
    {
        var parts = Name.Split(" ");
        return (parts.Length > 0) ? parts[0] : throw new InvalidOperationException("No name!");
    }
    public string GetLastName() => throw new NotImplementedException();
}

編集:

この質問を更新して、throwを式本体のメンバー、3項式、およびnull合体式の式として使用する方法に関する新しい情報へのリンクを追加します。C#7がリリースされました。

C#7の新機能-式をスローします

C#7.0の新機能

15
Curtis Lusmore

理由についての答えではなく、回避策:

void Method3() => ThrowNotImplemented();

int Method4() => ThrowNotImplemented<int>();

private static void ThrowNotImplemented()
{
    throw new NotImplementedException();
}

private static T ThrowNotImplemented<T>()
{
    throw new NotImplementedException();
}
1
Johan Larsson

Jeroen Vannevelが説明したように、式は式本体のメンバーにのみ使用できます。これはお勧めしませんが、ラムダ式を適切な型にキャストして呼び出すことで、(複雑な)コードを式にカプセル化することができます。

public void Method3() => ((Action)(() => { throw new Exception(); })).Invoke();

このようにして、式本体のメンバーの1行で例外をスローできます。

おそらく、これを行わないのには十分な理由があります。しかし、表現本体のメンバーがこのような表現に限定され、この例のように回避できるのは設計上の欠陥だと思います。

1
Felix Keil

古いスレッドですが、C#はC#7で追加されたスロー式をサポートするようになりました。

以前は、

var colorString = "green,red,blue".Split(',');
var colors = (colorString.Length > 0) ? colorString : null
if(colors == null){throw new Exception("There are no colors");}

もういや。さて、ヌル合体演算子:

var firstName = name ?? throw new ArgumentException ();

として条件演算子:

条件演算子でも可能です。

var arrayFirstValue = (array.Length > 0)? array[1] : 
  throw new Expection("array contains no elements");

式本体メンバー:

public string GetPhoneNumber () => throw new NotImplementedException();
0
Gauravsa