C#中的属性签名是什么?
我遇到了一些代码说
public int MaxHealth => Memory[Address].IsValid ? Memory[Address].Read<int>(Offs.Life.MaxHp) : 0;
现在我对Lambdaexpression式有些熟悉了。 我只是没有看到它用这种方式。
上述声明和声明之间的区别是什么?
public int MaxHealth = x ? y:z;
你正在看的是一个expression式的成员 ,而不是一个lambdaexpression式。
当编译器遇到一个expression式属性成员时,它将会基本上将它转换成get
ter,如下所示:
public int MaxHealth { get { return Memory[Address].IsValid ? Memory[Address].Read<int>(Offs.Life.MaxHp) : 0; } }
(您可以通过将代码抽取到名为TryRoslyn的工具来validation这一点 。)
与大多数C#6特性一样,expression式成员只是 语法上的糖 。 这意味着它们不提供通过现有function无法实现的function。 相反,这些新function允许使用更具expression性和简洁性的语法
正如你所看到的,expression式的成员有一些捷径可以使财产成员更加紧凑:
- 没有必要使用
return
语句,因为编译器可以推断出你想返回expression式的结果 - 没有必要创build一个语句块,因为它只是一个expression式
- 没有必要使用
get
关键字,因为它使用expression式 – 体式成员语法。
我最后一点是大胆的,因为这与你的实际问题有关,我现在要回答。
和…之间的不同…
// expression-bodied member property public int MaxHealth => x ? y:z;
和…
// field with field initializer public int MaxHealth = x ? y:z;
是相同的区别… … –
public int MaxHealth { get { return x ? y:z; } }
和…
public int MaxHealth = x ? y:z;
哪一个 – 如果你理解属性 – 应该是显而易见的。
但要清楚的是:第一个列表是一个吸气罩下的属性,每次访问时都会调用它。 第二个列表是一个带有字段初始值设定项的字段,当该types被实例化时,其expression式只被计算一次。
语法上的差异实际上是相当微妙的,并且可能导致比尔·瓦格纳在“AC#6窍门:初始化与expression身体成员”一文中描述的“陷阱” 。
尽pipeexpression式成员是lambdaexpression式,但它们不是 lambdaexpression式。 最根本的区别是lambdaexpression式导致委托实例或expression式树。 expression式成员只是编译器在后台生成属性的一个指令。 相似性(或多或less)以箭头开始和结束( =>
)。
我还要补充一点,expression式成员不限于财产成员。 他们在所有这些成员上工作:
- 属性
- 索引
- 方法
- 运营商
但是,他们不在这些成员上工作:
- 构造函数
- Deconstructors
- 嵌套types
- 活动
- 字段
这是C#6的一个新特性,称为expression式主体成员,允许您使用类似lambda函数来定义仅获取特定属性。
虽然它被认为是以下语法糖 ,但它们可能不会产生相同的IL:
public int MaxHealth { get { return Memory[Address].IsValid ? Memory[Address].Read<int>(Offs.Life.MaxHp) : 0; } }
事实certificate,如果您编译上述两个版本并比较每个版本生成的IL,您将看到它们几乎相同。
当在一个名为TestClass
的类中定义时,这个答案中的经典版本是IL:
.property instance int32 MaxHealth() { .get instance int32 TestClass::get_MaxHealth() } .method public hidebysig specialname instance int32 get_MaxHealth () cil managed { // Method begins at RVA 0x2458 // Code size 71 (0x47) .maxstack 2 .locals init ( [0] int32 ) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress> TestClass::Memory IL_0007: ldarg.0 IL_0008: ldfld int64 TestClass::Address IL_000d: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress>::get_Item(!0) IL_0012: ldfld bool MemoryAddress::IsValid IL_0017: brtrue.s IL_001c IL_0019: ldc.i4.0 IL_001a: br.s IL_0042 IL_001c: ldarg.0 IL_001d: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress> TestClass::Memory IL_0022: ldarg.0 IL_0023: ldfld int64 TestClass::Address IL_0028: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress>::get_Item(!0) IL_002d: ldarg.0 IL_002e: ldfld class Offs TestClass::Offs IL_0033: ldfld class Life Offs::Life IL_0038: ldfld int64 Life::MaxHp IL_003d: callvirt instance !!0 MemoryAddress::Read<int32>(int64) IL_0042: stloc.0 IL_0043: br.s IL_0045 IL_0045: ldloc.0 IL_0046: ret } // end of method TestClass::get_MaxHealth
下面是在名为TestClass
的类中定义的expression式成员版本的IL:
.property instance int32 MaxHealth() { .get instance int32 TestClass::get_MaxHealth() } .method public hidebysig specialname instance int32 get_MaxHealth () cil managed { // Method begins at RVA 0x2458 // Code size 66 (0x42) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress> TestClass::Memory IL_0006: ldarg.0 IL_0007: ldfld int64 TestClass::Address IL_000c: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress>::get_Item(!0) IL_0011: ldfld bool MemoryAddress::IsValid IL_0016: brtrue.s IL_001b IL_0018: ldc.i4.0 IL_0019: br.s IL_0041 IL_001b: ldarg.0 IL_001c: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress> TestClass::Memory IL_0021: ldarg.0 IL_0022: ldfld int64 TestClass::Address IL_0027: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress>::get_Item(!0) IL_002c: ldarg.0 IL_002d: ldfld class Offs TestClass::Offs IL_0032: ldfld class Life Offs::Life IL_0037: ldfld int64 Life::MaxHp IL_003c: callvirt instance !!0 MemoryAddress::Read<int32>(int64) IL_0041: ret } // end of method TestClass::get_MaxHealth
请参阅https://msdn.microsoft.com/en-us/magazine/dn802602.aspx以获取有关C#6中此function和其他新function的更多信息。;
看到这篇文章C#3.0 +中的属性和字段之间的区别字段和C#中的属性获得者之间的区别。
它被称为Expression Bodied Member ,它是在C#6中引入的。它仅仅是一个get
唯一属性的语法糖。
这相当于:
public int MaxHealth { get { return Memory[Address].IsValid ? Memory[Address].Read<int>(Offs.Life.MaxHp) : 0; }
相当于一个方法声明是可用的:
public string HelloWorld() => "Hello World";
主要让你缩短样板。
好吧…我发表了一个评论,他们是不同的,但不能解释究竟如何,但现在我知道。
String Property { get; } = "value";
是不一样的
Property => value
这是区别…
当您使用自动初始值设定项时,该属性将创build值的实例并持续使用该值。 在上面的post中,有一个与比尔·瓦格纳的链接断了,解释了这一点,我search了正确的链接,自己理解它。
在我的情况,我有我的财产自动初始化一个视图ViewModel命令。 我改变了属性使用expression式初始化和命令CanExecute停止工作。
这是它看起来像什么,这是发生了什么事。
Command MyCommand { get; } = new Command(); //works
这是我改变它。
Command MyCommand => new Command(); //doesn't work properly
这里的区别是我用{ get; } =
{ get; } =
我创build并引用该属性中的SAME命令。 当我使用=>
我实际上创build一个新的命令,并在每次调用属性时返回它。 因此,我永远不能在我的命令上更新CanExecute
,因为我总是告诉它更新该命令的新引用。
{ get; } = // same reference => // new reference
所有这一切说,如果你只是指向一个支持领域,那么它工作正常。 只有在auto或expression体创build返回值时才会发生这种情况。
其他重要的一点是'=>'可以用来代替'get', 只用于'只有'方法 – 它不能和'set'一起使用。