IT story

속성 서명에서 C #의 => 할당은 무엇입니까?

hot-time 2020. 5. 1. 08:08
반응형

속성 서명에서 C #의 => 할당은 무엇입니까?


나는 몇 가지 코드를 발견했다.

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

이제 나는 Lambda 표현에 어느 정도 익숙합니다. 나는 그것이 이런 식으로 사용되는 것을 보지 못했습니다.

위의 진술과 차이점은 무엇입니까?

public int MaxHealth  = x ? y:z;

당신이보고있는 것은 람다식이 아닌 식 본문 멤버 입니다.

컴파일러가 식 본문 속성 멤버를 만나면 본질적으로 다음과 같이 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가있는 자산입니다. 두 번째 목록은 유형이 인스턴스화 될 때 표현식이 한 번만 평가되는 필드 이니셜 라이저가있는 필드입니다.

구문의 이러한 차이는 실제로 매우 미묘하며 "AC # 6 gotcha : Initialization vs. Expression Bodied Members" 라는 제목의 게시물에서 Bill Wagner가 설명하는 "gotcha"로 이어질 수 있습니다 .

While expression-bodied members are lambda expression-like, they are not lambda expressions. The fundamental difference is that a lambda expression results in either a delegate instance or an expression tree. Expression-bodied members are just a directive to the compiler to generate a property behind the scenes. The similarity (more or less) starts and end with the arrow (=>).

I'll also add that expression-bodied members are not limited to property members. They work on all these members:

  • Properties
  • Indexers
  • Methods
  • Operators

Added in C# 7.0

However, they do not work on these members:

  • Nested Types
  • Events
  • Fields

This is a new feature of C# 6 called an expression bodied member that allows you to define a getter only property using a lambda like function.

While it is considered syntactic sugar for the following, they may not produce identical IL:

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

It turns out that if you compile both versions of the above and compare the IL generated for each you'll see that they are NEARLY the same.

Here is the IL for the classic version in this answer when defined in a class named 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

And here is the IL for the expression bodied member version when defined in a class named 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

See https://msdn.microsoft.com/en-us/magazine/dn802602.aspx for more information on this and other new features in C# 6.

See this post Difference between Property and Field in C# 3.0+ on the difference between a field and a property getter in C#.

Update:

Note that expression-bodied members were expanded to include properties, constructors, finalizers and indexers in C# 7.0.


Ok... I made a comment that they were different but couldn't explain exactly how but now I know.

String Property { get; } = "value";

is not the same as

String Property => "value";

Here's the difference...

When you use the auto initializer the property creates the instance of value and uses that value persistently. In the above post there is a broken link to Bill Wagner, that explains this well, and I searched the correct link to understand it myself.

In my situation I had my property auto initialize a command in a ViewModel for a View. I changed the property to use expression bodied initializer and the command CanExecute stopped working.

Here's what it looked like and here's what was happening.

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

here's what I changed it to.

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

The difference here is when I use { get; } = I create and reference the SAME command in that property. When I use => I actually create a new command and return it every time the property is called. Therefore, I could never update the CanExecute on my command because I was always telling it to update a new reference of that command.

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

All that said, if you are just pointing to a backing field then it works fine. This only happens when the auto or expression body creates the return value.


It is called Expression Bodied Member and it was introduced in C# 6. It is merely syntactic sugar over a get only property.

It is equivalent to:

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

An equivalent of a method declaration is avaliable:

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

Mainly allowing you shortening of boilerplate.


One other significant point if you're using C# 6:

'=>' can be used instead of 'get' and is only for 'get only' methods - it can't be used with a 'set'.

For C# 7, see the comment from @avenmore below - it can now be used in more places. Here's a good reference - https://csharp.christiannagel.com/2017/01/25/expressionbodiedmembers/

참고URL : https://stackoverflow.com/questions/31764532/what-is-the-assignment-in-c-sharp-in-a-property-signature

반응형