我看到一些代码说

public int MaxHealth => 
         Memory[Address].IsValid ? 
         Memory[Address].Read<int>(Offs.Life.MaxHp) : 
         0;

现在我对Lambda表达式有点熟悉了。我只是没见过它这样使用。

上述陈述和两者之间的区别是什么

public int MaxHealth  = x ? y:z;

当前回答

这是c# 6的一个新特性,称为表达式体成员,它允许您使用类lambda函数定义仅为getter的属性。

虽然它被认为是以下语法糖,但它们可能不会产生相同的IL:

public int MaxHealth
{
    get
    {
        return Memory[Address].IsValid
               ?   Memory[Address].Read<int>(Offs.Life.MaxHp)
               :   0;
    }
}

事实证明,如果您编译上述两个版本并比较为每个版本生成的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的类中定义的表达式体成员版本的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

有关c# 6中的这个特性和其他新特性的更多信息,请参阅https://msdn.microsoft.com/en-us/magazine/dn802602.aspx。

请参阅c# 3.0+中属性和字段的区别这篇关于c#中字段和属性getter之间区别的文章。

更新:

注意,在c# 7.0中,表达式体成员被扩展到包括属性、构造函数、终结器和索引器。

其他回答

这是c# 6的一个新特性,称为表达式体成员,它允许您使用类lambda函数定义仅为getter的属性。

虽然它被认为是以下语法糖,但它们可能不会产生相同的IL:

public int MaxHealth
{
    get
    {
        return Memory[Address].IsValid
               ?   Memory[Address].Read<int>(Offs.Life.MaxHp)
               :   0;
    }
}

事实证明,如果您编译上述两个版本并比较为每个版本生成的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的类中定义的表达式体成员版本的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

有关c# 6中的这个特性和其他新特性的更多信息,请参阅https://msdn.microsoft.com/en-us/magazine/dn802602.aspx。

请参阅c# 3.0+中属性和字段的区别这篇关于c#中字段和属性getter之间区别的文章。

更新:

注意,在c# 7.0中,表达式体成员被扩展到包括属性、构造函数、终结器和索引器。

你也可以使用箭头作为get访问器:

    private string foo = "foo";

    private string bar
    {
        get => $"{foo}bar";
        set
        {
            foo = value;
        }
    }

以下是亚历克斯·布克在他们的回答中分享的声明

当编译器遇到表达式体的属性成员时,它本质上是将其转换为这样的getter:

请看下面的截图,它展示了这个语句(使用SharpLab链接)

public string APIBasePath => Configuration.ToolsAPIBasePath;

皈依

public string APIBasePath
{
    get
    {
        return Configuration.ToolsAPIBasePath;
    }
}

截图:

您现在看到的是一个表达式体成员,而不是lambda表达式。

当编译器遇到表达式体的属性成员时,它本质上是将其转换为这样的getter:

public int MaxHealth
{
    get
    {
        return Memory[Address].IsValid ? Memory[Address].Read<int>(Offs.Life.MaxHp) : 0;
    }
}

(您可以通过将代码输入TryRoslyn工具来验证这一点。)

表达式体成员——像大多数c# 6特性一样——只是语法糖。这意味着它们不能提供通过现有特性无法实现的功能。相反,这些新特性允许使用更具表现力和更简洁的语法

正如你所看到的,表达式体成员有一些快捷方式,使属性成员更加紧凑:

不需要使用return语句,因为编译器可以推断出您想要返回表达式的结果 不需要创建语句块,因为语句体只是一个表达式 不需要使用get关键字,因为它是由表达式体成员语法的使用暗示的。

我把最后一点加粗了,因为这与你的实际问题有关,我现在就回答你的问题。

……之间的区别。

// 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;

如果你了解属性,这应该是显而易见的。

不过,需要明确的是:第一个清单是一个在底层带有getter的属性,每次访问它时都会调用它。第二个清单是一个带有字段初始化器的字段,当类型实例化时,它的表达式只计算一次。

语法上的这种差异实际上是非常微妙的,可能会导致一个“陷阱”,Bill Wagner在一篇题为“c# 6陷阱:初始化与表达式体成员”的文章中描述了这一点。

虽然表达式体成员类似lambda表达式,但它们不是lambda表达式。基本的区别在于lambda表达式的结果要么是委托实例,要么是表达式树。表达式体成员只是指示编译器在幕后生成属性的指令。相似度(或多或少)以箭头(=>)开始和结束。

我还要补充一点,表达体成员并不局限于属性成员。它们对所有这些成员都起作用:

属性 索引器 方法 运营商

在c# 7.0中添加

构造函数 终结器

但是,它们对以下成员不起作用:

嵌套类型 事件 字段

它被称为表达式体成员,是在c# 6中引入的。它只是get only属性上的语法修饰。

它相当于:

public int MaxHealth { get { return Memory[Address].IsValid ?
                             Memory[Address].Read<int>(Offs.Life.MaxHp) : 0; }

一个等效的方法声明是可用的:

public string HelloWorld() => "Hello World";

主要是让你缩短样板。