it-swarm.com.de

Warum kann ich keine Ausnahmen von einem Mitglied mit Ausdruck werfen?

Wenn Sie Elemente mit Ausdruck verwenden, können Sie den Hauptteil einer Methode oder Eigenschaft als einen einzelnen Ausdruck ohne ein Return-Schlüsselwort definieren (falls etwas zurückgegeben wird).

Zum Beispiel dreht es diese

int Method1()
{
    return 5;
}

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

in diese

int Method1() => 5;

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

Ein Unterschied kommt ins Spiel, wenn Sie eine Ausnahme aus dem Körper werfen:

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

Folgendes wird jedoch nicht kompiliert:

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

mit folgenden Meldungen:

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' 

Warum?

28
Jeroen Vannevel

Dies geschieht, weil die ersten beiden Codeausschnitte (5 und Console.WriteLine) Ausdrücke sind. Dies sind insbesondere NumericLiteralExpression und InvocationExpression.

Letzteres (throw new Exception()) ist eine Anweisung - in diesem Fall: ThrowStatement.

Wenn Sie sich das Roslyn-SDK ansehen, werden Sie feststellen, dass ein MethodDeclarationSyntax-Objekt eine Eigenschaft ExpressionBody vom Typ ArrowExpressionClauseSyntax hat, die wiederum eine Eigenschaft des Typs ExpressionSyntax besitzt. Dies sollte offensichtlich machen, dass nur Ausdrücke in einem Ausdruckskörper-Member akzeptiert werden.

Wenn Sie sich das letzte Codebeispiel ansehen, werden Sie feststellen, dass es aus einer ThrowStatementSyntax besteht, die wiederum eine ExpressionSyntax-Eigenschaft besitzt. In unserem Fall füllen wir das mit einem ObjectCreationExpressionSyntax-Objekt.


Was ist der Unterschied zwischen einem Ausdruck und einer Aussage?

Warum akzeptiert es auch keine Aussagen?

Ich kann nur vermuten, aber ich würde davon ausgehen, dass dies zu viele Nebeneffekte eröffnen würde, nur um eine Ausnahme auslösen zu können. Ich glaube nicht, dass ein Ausdruck und eine Anweisung einen gemeinsamen Vorfahren in der Vererbung haben. Es würde also eine Menge Code-Duplizierung geben. Am Ende gehe ich davon aus, dass es einfach nicht wert ist, den Ärger zu sein, obwohl es irgendwie Sinn macht.

Wenn Sie einen einfachen Ausdruck als Teil eines Methodenkörpers schreiben, der tatsächlich unter einer ExpressionStatementSyntax verpackt wird - ja, beide kombiniert! Dies ermöglicht die Gruppierung mit anderen Anweisungen unter der Body-Eigenschaft der Methode. Unter der Haube müssen sie dies abrollen und den Ausdruck daraus extrahieren. Dies kann wiederum für das Element mit Ausdruckskörper verwendet werden, da zu diesem Zeitpunkt nur noch ein Ausdruck und keine Anweisung mehr vorhanden ist.

Ein wichtiger Hinweis hierbei ist jedoch die Tatsache, dass eine return-Anweisung .. eine Anweisung ist. Genauer gesagt eine ReturnStatementSyntax. Sie müssen dies explizit behandelt und die Compiler-Magie angewendet haben, obwohl dies die Frage aufwirft: Warum nicht dasselbe für ThrowStatementSyntax?

Stellen Sie sich das folgende Szenario vor: plötzlich werden auch throw-Anweisungen akzeptiert. Da ein Element mit Ausdruckskörper jedoch nur Ausdrücke als Hauptteil (duh) enthalten kann, müssen Sie das Schlüsselwort throw weglassen und verbleiben stattdessen bei new Exception(). Wie werden Sie zwischen einer return-Anweisung und einer throw-Anweisung unterscheiden?

Es gibt keinen Unterschied zwischen der ausdrucksabhängigen Variante dieser beiden Methoden:

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

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

Sowohl eine throw- als auch eine return-Anweisung sind gültige Methodenende. Wenn Sie sie jedoch weglassen, unterscheidet sich nichts von dem Zwei-ergo: Sie würden nie wissen, ob Sie das neu erstellte Exception-Objekt zurückgeben oder auslösen sollen.

Was soll ich davon nehmen?

Ein Element mit Ausdruckskörper ist genau so, wie der Name sagt: ein Mitglied, das nur einen Ausdruck in seinem Körper hat. Das bedeutet, dass Sie wissen müssen, was genau einen Ausdruck ausmacht. Nur weil es sich um eine "Aussage" handelt, ist sie nicht Ausdruck.

34
Jeroen Vannevel

Diese Funktion ist in C # 7 verfügbar. Von https://blogs.msdn.Microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/

Es ist leicht, eine Ausnahme in die Mitte eines Ausdrucks zu werfen: Rufen Sie einfach eine Methode auf, die es für Sie erledigt! In C # 7.0 lassen wir jedoch throw an bestimmten Stellen direkt als Ausdruck zu:

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();
}

Bearbeiten:

Aktualisieren dieser Frage, um Links zu neueren Informationen darüber hinzuzufügen, wie throw jetzt als Ausdruck in ausdrucksabhängigen Elementen, ternären Ausdrücken und Null-Koaleszenz-Ausdrücken verwendet werden kann, nachdem C # 7 veröffentlicht wurde:

Was ist neu in C # 7 - Throw-Ausdrücke .

Neue Funktionen in C # 7.0 .

15
Curtis Lusmore

Keine Antwort auf das Warum, aber eine Problemumgehung:

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

Wie Jeroen Vannevel erklärt hat, können wir Ausdrücke nur für Mitglieder mit Ausdruckskörper verwenden. Ich würde dies nicht empfehlen, aber Sie können Ihren (komplexen) Code immer in einen Ausdruck einkapseln, indem Sie einen Lambda-Ausdruck in einen geeigneten Typ schreiben und rufen Sie es auf.

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

Auf diese Weise können Sie immer noch eine Ausnahme in einem Ausdruck mit körperlichem Member auslösen.

Wahrscheinlich gibt es gute Gründe, dies nicht zu tun. Aber ich denke, es ist ein Designfehler, dass Ausdrucksmitglieder nur auf solche Ausdrücke beschränkt sind und wie in diesem Beispiel umgearbeitet werden können.

1
Felix Keil

Es ist zwar ein alter Thread, aber C # unterstützt jetzt Wurfausdrücke, die in C # 7 hinzugefügt wurden. 

Vorher,

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

Nicht mehr. Nun als Nullkoaleszenzoperator:

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

Als Bedingter Operator:

Es ist auch im bedingten Operator möglich.

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

Ausdruckskörperiges Mitglied:

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