L'utilisation de membres dotés d'une expression vous permet de définir le corps d'une méthode ou d'une propriété comme une expression unique sans mot-clé return (si elle renvoie quelque chose).
Par exemple, il tourne ces
int Method1()
{
return 5;
}
void Method2()
{
Console.WriteLine();
}
dans ces
int Method1() => 5;
void Method2() => Console.WriteLine();
Une différence entre en jeu lorsque vous lancez une exception du corps:
void Method3()
{
throw new Exception();
}
Toutefois, les éléments suivants ne seront pas compilés:
void Method3() => throw new Exception();
avec les messages suivants:
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'
Pourquoi?
Cela est dû au fait que les deux premiers extraits de code (5
et Console.WriteLine
) sont des expressions. Plus spécifiquement, il s'agit respectivement de NumericLiteralExpression
et InvocationExpression
.
La dernière (throw new Exception()
) est une déclaration - dans ce cas: ThrowStatement
.
Si vous examinez le SDK de Roslyn, vous remarquerez qu'un objet MethodDeclarationSyntax
a une propriété ExpressionBody
de type ArrowExpressionClauseSyntax
qui, à son tour, a une propriété de type ExpressionSyntax
. Cela devrait rendre évident que seules les expressions sont acceptées dans un membre contenant une expression.
Si vous examinez le dernier exemple de code, vous remarquerez qu'il se compose d'une ThrowStatementSyntax
qui possède à son tour une propriété ExpressionSyntax
. Dans notre cas, nous remplissons cela avec un objet ObjectCreationExpressionSyntax
.
Je ne peux que deviner, mais je suppose que c’est parce que cela entraînerait beaucoup trop d’effets secondaires simplement pour pouvoir lancer une exception. Je ne crois pas qu'une expression et une déclaration aient un ancêtre commun dans l'héritage, il y aurait donc beaucoup de duplication de code. En fin de compte, je suppose que cela n’a tout simplement pas valu la peine d’être un problème, même si cela a un sens.
Lorsque vous écrivez une expression simple dans le cadre d'un corps de méthode qui est en fait enveloppé par une ExpressionStatementSyntax
- oui, les deux sont combinés! Cela lui permet d'être regroupé avec d'autres instructions sous la propriété Body
de la méthode. Sous le capot, ils doivent dérouler cela et en extraire l'expression. Ceci peut à son tour être utilisé pour le membre avec une expression car à ce stade, il ne vous reste plus qu'une expression et non plus une instruction.
Une remarque importante ici cependant est le fait qu'une déclaration de retour est une déclaration. Plus spécifiquement une ReturnStatementSyntax
. Ils doivent avoir manipulé cela explicitement et appliqué la magie du compilateur, ce qui soulève la question: pourquoi ne pas faire la même chose pour ThrowStatementSyntax
?
Considérez le scénario suivant: tout à coup, les instructions throw
sont également acceptées. Cependant, puisqu'un membre avec une expression ne peut avoir que des expressions en tant que corps (duh), cela signifie que vous devez omettre le mot clé throw
et laisser à la place la propriété new Exception()
. Comment allez-vous faire la distinction entre l'intention d'utiliser une instruction return
et une instruction throw
?
Il n'y aurait pas de différence entre la variation de l'expression de ces deux méthodes:
public Exception MyMethod()
{
return new Exception();
}
public Exception MyMethod()
{
throw new Exception();
}
Les instructions throw
et return
sont des terminaisons de méthodes valides. Cependant, lorsque vous les omettez, rien ne distingue les deux - ergo: vous ne saurez jamais s'il faut revenir ou jeter cet objet exception nouvellement créé.
Un membre avec une expression est exactement comme son nom l'indique: un membre avec seulement une expression dans son corps. Cela signifie que vous devez être conscient de ce qui constitue exactement une expression. Le simple fait que ce soit une "déclaration" n'en fait pas une expression.
Cette fonctionnalité arrive en C # 7. De https://blogs.msdn.Microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/
Il est facile de lancer une exception au milieu d’une expression: il suffit d’appeler une méthode qui le fait pour vous! Mais en C # 7.0, nous autorisons directement
throw
comme expression à certains endroits:
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();
}
Modifier:
Mise à jour de cette question pour ajouter des liens vers des informations plus récentes sur la manière dont throw
peut maintenant être utilisé comme expression dans les membres dotés d'expression, les expressions ternaires et les expressions à fusion nulle, maintenant que C # 7 est publié:
Pas une réponse sur pourquoi mais une solution de contournement:
void Method3() => ThrowNotImplemented();
int Method4() => ThrowNotImplemented<int>();
private static void ThrowNotImplemented()
{
throw new NotImplementedException();
}
private static T ThrowNotImplemented<T>()
{
throw new NotImplementedException();
}
Comme Jeroen Vannevel l'a expliqué, nous ne pouvons utiliser que des expressions pour les membres dotés d'une expression. Je ne le recommanderais pas, mais vous pouvez toujours encapsuler votre code (complexe) dans une expression en écrivant une expression lambda en le transformant en un type approprié. et l'invoquer.
public void Method3() => ((Action)(() => { throw new Exception(); })).Invoke();
De cette façon, vous pouvez toujours lancer une exception dans une ligne d'un membre avec une expression!
Il y a probablement de bonnes raisons de ne pas le faire. Mais je pense que le fait que les membres dotés d'une expression soient restreints à des expressions comme celle-ci est un défaut de conception, et peut être contourné comme dans cet exemple.
Bien que ce soit un vieux fil, mais C # supporte maintenant les expressions de jet ajoutées dans C # 7.
Précédemment,
var colorString = "green,red,blue".Split(',');
var colors = (colorString.Length > 0) ? colorString : null
if(colors == null){throw new Exception("There are no colors");}
Pas plus. Maintenant, en tant que Opérateur de coalescence nul:
var firstName = name ?? throw new ArgumentException ();
En tant que Opérateur conditionnel:
C'est également possible dans l'opérateur conditionnel.
var arrayFirstValue = (array.Length > 0)? array[1] :
throw new Expection("array contains no elements");
Membre avec corps d'expression:
public string GetPhoneNumber () => throw new NotImplementedException();