【问题标题】:What is the => assignment in C# in a property signature什么是属性签名中 C# 中的 => 赋值
【发布时间】:2015-08-01 17:51:56
【问题描述】:

我遇到了一些代码

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

现在我对 Lambda 表达式有些熟悉了。我只是没有看到它这样使用它。

上面的语句和

有什么区别
public int MaxHealth  = x ? y:z;

【问题讨论】:

  • 第一个块是属性第二个是变量
  • @M.kazemAkhgary *一个字段,不是一个变量。

标签: c# c#-6.0


【解决方案1】:

您正在查看的是 expression-bodied member 而不是 lambda 表达式。

当编译器遇到表达式主体的 property 成员时,它本质上会将其转换为如下所示的 getter:

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

(您可以通过将代码输入到名为TryRoslyn 的工具中自己验证这一点。)

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

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

  • 无需使用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 在题为 "A C# 6 gotcha: Initialization vs. Expression Bodied Members" 的帖子中描述的“陷阱”。

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

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

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

添加于C# 7.0

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

  • 嵌套类型
  • 活动
  • 字段

【讨论】:

  • @bzier 这是一个让我们成为函数式程序员的阴谋。 IF THEN ELSE 永远!!
  • Bill Wagner 帖子的链接目前已损坏。我想我找到了新的网址:codeproject.com/Articles/1064964/…
  • @Alex Booker 您或其他人能否解释一下您提供的第一个链接中public class RgbColor(int r, int g, int b) { ... }public int Red { get; } = r; 的语法?
  • 现在是 2021 年(或者,我喜欢这样称呼它:“2020 年第 2 部分:它仍然很烂。”)我是唯一一个认为这些事情是相当愚蠢的人,以避免输入一些括号以及一个像样的 IDE 会为您生成的大括号,无论如何,代价是可能会混淆 ==&gt; 以及由此产生的错误?在出现错误的情况下很容易调试,但是......增加的价值在哪里?
【解决方案2】:

=&gt; 语法等于get { return ... } 语法。

String Property { get; } = "value";

不一样

String Property => "value";

这就是区别...

当您使用自动初始化器时,该属性会创建值的实例并持续使用该值。在上面的帖子中,Bill Wagner 的链接断开了,这很好地解释了这一点,我自己搜索了正确的链接来理解它。

在我的情况下,我让我的属性在 ViewModel 中为视图自动初始化命令。我将属性更改为使用表达式主体初始化程序,并且命令 CanExecute 停止工作。

这就是它的样子,这就是正在发生的事情。

Command MyCommand { get; } = new Command();  //works

这是我改成的。

Command MyCommand => new Command();  //doesn't work properly

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

{ get; } = // same reference
=>         // new reference

话虽如此,如果您只是指向一个支持字段,那么它就可以正常工作。这只发生在自动或表达式主体创建返回值时。

【讨论】:

    【解决方案3】:

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

    虽然以下情况被视为 syntactic sugar,但它们可能不会产生相同的 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# 中字段和属性 getter 之间的区别,请参阅这篇文章 Difference between Property and Field in C# 3.0+

    更新:

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

    【讨论】:

      【解决方案4】:

      它被称为 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";
      

      主要允许您缩短样板。

      【讨论】:

      • 最后一行和public string HelloWorld =&gt; "Hello World";一样吗?为什么是圆括号?
      • @Jenix 在这种情况下,它是一种方法而不是属性。你会在其他地方引用它,比如var someString = Foo.HelloWorld(); 我看到的唯一原因是,如果你以后可能想在那里添加一些作为属性没有意义的代码,或者你正在覆盖一个方法。
      • @3Dave 谢谢。不知道我们也可以声明这样的方法。我们可以从哪个版本开始使用它?
      【解决方案5】:

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

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

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

      【讨论】:

      • 如果您使用 C#7,则不再适用。"C# 7.0 继续提高生产力。表达式体成员已在 C# 6 中用于方法和属性,现在它们可以与构造函数一起使用,析构函数、属性访问器和事件访问器。” (Source)
      【解决方案6】:

      对于Alex Bookertheir answer中分享的以下语句

      当编译器遇到表达式体属性成员时,它本质上会将其转换为如下所示的 getter:

      请看下面的截图,它显示了这个语句是如何的(使用SharpLab link

      public string APIBasePath => Configuration.ToolsAPIBasePath;
      

      转换为

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

      截图:

      【讨论】:

        【解决方案7】:

        您还可以使用get 访问器的箭头:

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

        【讨论】:

        • 您的回答不是对问题的回答。请添加详细信息以与问题更相关。
        • @hostel 我觉得有道理
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-04
        相关资源
        最近更新 更多