使用表达式成员允许您将方法或属性的主体定义为没有return关键字的单个表达式(如果它返回了某些内容)。
例如,它会变成这些
autoforms = []
part_formsets = []
autos = Auto.objects.all()
PartFormSet = inlineformset_factory(Auto, Part, fields=('field1', 'field2'))
for auto in autos:
autoform = AutoForm(instance=auto)
autoforms.append(autoform)
part_formset = PartFormSet(instance=auto)
part_formsets.append(part_formset)
c = {'autoforms': autoforms, 'part_formsets': part_formsets}
return render(request, 'some.html', c)
进入这些
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();
为什么?
答案 0 :(得分:35)
这是因为前两个代码段(5
和Console.WriteLine
)是表达式。更具体地说,这些分别是NumericLiteralExpression
和InvocationExpression
。
后一个(throw new Exception()
)是一个陈述 - 在这种情况下:ThrowStatement
。
如果您查看Roslyn SDK,您会注意到MethodDeclarationSyntax
对象的属性ExpressionBody
类型为ArrowExpressionClauseSyntax
,而ExpressionSyntax
属性类型为ThrowStatementSyntax
。这应该很明显只有表达式成员才能接受表达式。
如果您查看最后一个代码示例,您会发现它包含ExpressionSyntax
,而ObjectCreationExpressionSyntax
又具有ExpressionStatementSyntax
属性。在我们的例子中,我们用Body
对象填充它。
我只能在这里猜测,但我认为这是因为这会打开太多的副作用,只是为了能够抛出异常。我不相信表达式和语句在继承中有共同的祖先,所以会有很多代码重复。最后,我认为它归结为简直不值得麻烦,尽管它在某种程度上是有道理的。
当你将一个简单的表达式写成一个实际上包含在ReturnStatementSyntax
下的方法体的一部分时 - 是的,两者结合在一起!这允许它与方法的ThrowStatementSyntax
属性下的其他语句组合在一起。在引擎盖下,他们必须展开它并从中提取表达式。这反过来可以用于表达式身体成员,因为此时你只剩下一个表达式而不再是一个语句。
然而,一个重要的注意事项是,return语句是一个声明。更具体地说是throw
。他们必须已经明确地处理了这个并且应用了编译器魔术,虽然它提出了一个问题:为什么不对throw
做同样的事情?
请考虑以下情形:突然,new Exception()
语句也被接受。但是,由于表达式身体成员只能将表达式作为其主体(duh),这意味着您必须省略return
关键字,而是留下throw
。您如何区分打算public Exception MyMethod()
{
return new Exception();
}
public Exception MyMethod()
{
throw new Exception();
}
语句和throw
语句?
这两种方法的表达体变异之间没有区别:
return
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.amazonaws.services.lambda.runtime.Context;
public class MyHandler {
public void handler(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int letter;
while((letter = inputStream.read()) != -1)
{
baos.write(letter);
}
//Send the contents of baos to a JSON deserializer ...
}
}
和{{1}}语句都是有效的方法结尾。但是当你省略它们时,没有什么可以区分两者 - ergo:你永远不会知道是返回还是抛出新创建的异常对象。
一个表达体的成员正如名字所说的那样:一个在其体内只有一个表达式的成员。这意味着您必须了解表达式的确切构成。仅仅因为它是一个“陈述”并不能使它成为一种表达。
答案 1 :(得分:15)
此功能将在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
现在可以用作表达式身体成员,三元表达式和空合并表达式中的表达式的更新信息的链接,现在C#7已发布:
答案 2 :(得分:1)
不是解决原因的答案,而是解决方法:
void Method3() => ThrowNotImplemented();
int Method4() => ThrowNotImplemented<int>();
private static void ThrowNotImplemented()
{
throw new NotImplementedException();
}
private static T ThrowNotImplemented<T>()
{
throw new NotImplementedException();
}
答案 3 :(得分:1)
正如Jeroen Vannevel所解释的那样,我们只能将表达式用于表达身体的成员。 我不建议这样做,但你总是可以通过编写一个lambda表达式将你的(复杂)代码封装到一个表达式中,并将其转换为适当的类型并调用它。
public void Method3() => ((Action)(() => { throw new Exception(); })).Invoke();
这样你仍然可以在表达式bodied成员的一行中抛出异常!
可能有充分的理由不这样做。 但我认为这是一个设计缺陷,表达身体的成员仅限于这样的表达式,可以在这个例子中解决。
答案 4 :(得分:0)
尽管它是一个旧线程,但是C#现在支持在C#7中添加的throw表达式。
以前,
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();