在属性签名中C#中的=>赋值是什么

Mik*_*ike 213 c# c#-6.0

我遇到了一些代码

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

现在我对Lambda表达式有点熟悉了.我只是没有看到它以这种方式使用它.

上述陈述与之间的区别是什么?

public int MaxHealth  = x ? y:z;
Run Code Online (Sandbox Code Playgroud)

Ale*_*ker 349

你所看到的是一个表达身体的成员 而不是lambda表达.

当编译器遇到表达式身体属性成员时,它实际上将它转换为这样的getter:

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

(您可以通过将代码泵入名为TryRoslyn的工具来自行验证.)

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

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

  • 不需要使用return语句,因为编译器可以推断您要返回表达式的结果
  • 无需创建语句块,因为正文只有一个表达式
  • 不需要使用get关键字,因为使用表达式 - 身体成员语法暗示了这一点.

我最后一点大胆,因为它与你的实际问题有关,我现在将回答.

和...之间的不同...

// expression-bodied member property
public int MaxHealth => x ? y:z;
Run Code Online (Sandbox Code Playgroud)

和...

// field with field initializer
public int MaxHealth = x ? y:z;
Run Code Online (Sandbox Code Playgroud)

两者之间的区别是一样的......

public int MaxHealth
{
    get
    {
        return x ? y:z;
    }
}
Run Code Online (Sandbox Code Playgroud)

和...

public int MaxHealth = x ? y:z;
Run Code Online (Sandbox Code Playgroud)

哪个 - 如果您了解属性 - 应该是显而易见的.

但需要明确的是:第一个列表是一个带有吸气剂的属性,每次访问它时都会调用它.第二个列表是一个带有字段初始值设定项的字段,当实例化类型时,其表达式仅计算一次.

语法上的这种差异实际上非常微妙,并且可能导致Bill Wagner在题为"AC#6陷阱:初始化与表达身体成员"的帖子中描述的"陷阱".

虽然表达健全成员是拉姆达expression- ,它们是 lambda表达式.根本区别在于lambda表达式会导致委托实例或表达式树.表达式成员只是编译器在幕后生成属性的指令.相似性(或多或少)以箭头(=>)开始和结束.

我还要补充说,表达身体的成员不仅限于财产成员.他们为所有这些成员工作:

  • 属性
  • 索引
  • 方法
  • 运营商

但是,它们不适用于这些成员:

  • 构造函数
  • Deconstructors
  • 嵌套类型
  • 活动
  • 字段

  • @bzier这是一个让我们成为功能性程序员的阴谋.如果那么永远! (8认同)
  • 从C#7开始,也支持构造函数和终结器.https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members (6认同)
  • 现在是 2021 年(或者,我喜欢这样称呼它:“2020 年第 2 部分:它仍然很糟糕。”)我是唯一一个认为这些事情是相当愚蠢的人,以避免输入一些像样的 IDE 会生成的括号和大括号吗?无论如何,对你来说,代价是可能将 `=` 与 `=&gt;` 混淆并产生错误?在出现错误的情况下很容易进行调试,但是......附加值在哪里? (3认同)
  • Bill Wagner的帖子的链接当前已断开。我想我找到了新的网址:https://www.codeproject.com/Articles/1064964/A-Csharp-Gotcha-Initialization-vs-Expression-Bodie (2认同)

Tyr*_*son 34

这是C#6的一个新功能,称为表达式身体成员,允许您使用类似lambda的函数定义一个仅具有getter的属性.

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

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

事实证明,如果你编译上面的两个版本并比较为每个版本生成的IL,你会发现它们几乎是相同的.

当在一个名为的类中定义时,这是本答案中经典版本的IL TestClass:

.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
Run Code Online (Sandbox Code Playgroud)

以下是在名为class的类中定义的表达bodied成员版本的IL TestClass:

.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
Run Code Online (Sandbox Code Playgroud)

有关C#6中的此功能和其他新功能的详细信息,请参阅https://msdn.microsoft.com/en-us/magazine/dn802602.aspx.

有关C#中字段和属性getter之间差异的信息,请参阅此文章C#3.0+中Property和Field之间的差异.

更新:

请注意,表达式成员已扩展为包含C#7.0中的属性,构造函数,终结符和索引器.


Mic*_* II 29

好的......我发表评论说他们不一样,但无法确切地解释如何,但现在我知道了.

String Property { get; } = "value";
Run Code Online (Sandbox Code Playgroud)

是不一样的

String Property => "value";
Run Code Online (Sandbox Code Playgroud)

这是区别......

使用auto初始化程序时,属性会创建value实例并持久使用该值.在上面的帖子中,有一个与Bill Wagner断开的链接,这很好地解释了这一点,我搜索了正确的链接以便自己理解.

在我的情况下,我让我的属性自动初始化ViewModel中的命令.我更改了属性以使用表达式bodied初始化程序并且命令CanExecute停止工作.

这就是它的样子,这就是发生的事情.

Command MyCommand { get; } = new Command();  //works
Run Code Online (Sandbox Code Playgroud)

这就是我改变它的原因.

Command MyCommand => new Command();  //doesn't work properly
Run Code Online (Sandbox Code Playgroud)

这里的区别在于我使用{ get; } =I创建并引用该属性中的SAME命令.当我使用时,=>我实际创建一个新命令,并在每次调用属性时返回它.因此,我永远无法更新CanExecuteon my命令,因为我总是告诉它更新该命令的新引用.

{ get; } = // same reference
=>         // new reference
Run Code Online (Sandbox Code Playgroud)

所有这一切,如果你只是指向一个支持领域,那么它工作正常.仅当auto或表达式主体创建返回值时才会发生这种情况.

  • =>语法等于get {return new Command(); } 句法. (7认同)

Yuv*_*kov 16

它被称为表达身体成员,它是在C#6中引入的.它只是一个get唯一属性的语法糖.

它相当于:

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

可以使用等效的方法声明:

public string HelloWorld() => "Hello World";
Run Code Online (Sandbox Code Playgroud)

主要允许您缩短样板.


Chr*_*row 6

如果您使用的是C#6,还有一点很重要:

'=>'可以代替'get'使用,并且用于'get only'方法 -不能与'set'一起使用。

对于C#7,请参阅下面@avenmore中的注释-现在可以在更多地方使用它。这是一个很好的参考-https://csharp.christiannagel.com/2017/01/25/expressionbodiedmembers/

  • 如果您使用的是C#7,则不再适用。“ C#7.0继续提高了生产率。C#6中提供了表达式体成员用于方法和属性,现在它们可与构造函数,析构函数,属性访问器和事件访问器一起使用也一样。” ([来源](https://csharp.christiannagel.com/2017/01/25/expressionbodiedmembers/)) (8认同)