式本体のメンバーを使用すると、メソッドまたはプロパティの本体を、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'
どうして?
これは、最初の2つのコードスニペット(_5
_と_Console.WriteLine
_)が式であるために発生します。より具体的には、これらはそれぞれNumericLiteralExpression
とInvocationExpression
です。
後者(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:新しく作成された例外オブジェクトを返すかスローするかはわかりません。
式本体のメンバーは、その名前が示すとおり、本体に式のみを持つメンバーです。これは、式を正確に構成するものを認識している必要があることを意味します。それが一つの「ステートメント」であるからといって、それを表現にするわけではありません。
この機能は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がリリースされました。
理由についての答えではなく、回避策:
void Method3() => ThrowNotImplemented();
int Method4() => ThrowNotImplemented<int>();
private static void ThrowNotImplemented()
{
throw new NotImplementedException();
}
private static T ThrowNotImplemented<T>()
{
throw new NotImplementedException();
}
Jeroen Vannevelが説明したように、式は式本体のメンバーにのみ使用できます。これはお勧めしませんが、ラムダ式を適切な型にキャストして呼び出すことで、(複雑な)コードを式にカプセル化することができます。
public void Method3() => ((Action)(() => { throw new Exception(); })).Invoke();
このようにして、式本体のメンバーの1行で例外をスローできます。
おそらく、これを行わないのには十分な理由があります。しかし、表現本体のメンバーがこのような表現に限定され、この例のように回避できるのは設計上の欠陥だと思います。
古いスレッドですが、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();