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


229

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

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

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

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

public int MaxHealth  = x ? y:z;

4
먼저 블록 특성 초 하나가 가변 인
M.kazem Akhgary

14
@ M.kazemAkhgary * 변수가 아닌 필드.
Mafii

답변:


376

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

컴파일러가 식 본문 속성 멤버를 만나면 본질적으로 다음과 같이 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"로 이어질 수 있습니다 .

표현 바디 회원 람다 expression- 동안 같은 , 그들이 있습니다 하지 람다 식입니다. 근본적인 차이점은 람다식이 델리게이트 인스턴스 또는 식 트리를 생성한다는 것입니다. Expression-bodyed 멤버는 배후에서 속성을 생성하기위한 컴파일러의 지시문 일뿐입니다. 유사성 (더 많거나 적은)은 화살표 ( =>)로 시작하고 끝납니다 .

또한 표현형 멤버는 속성 멤버로 제한되지 않는다고 덧붙입니다. 그들은이 모든 멤버들을 위해 일합니다 :

  • 속성
  • 인덱서
  • 행동 양식
  • 연산자

C # 7.0 에서 추가됨

그러나 다음 멤버에서는 작동하지 않습니다.

  • 중첩 유형
  • 행사
  • 필드

6
C # 7부터는 생성자와 종료자가 지원됩니다. docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/…
bzier

8
@bzier 우리를 프로그래머로 만드는 것은 음모입니다. 그렇다면 영원히!
Sentinel


2
Bill Wagner의 게시물에 대한 링크가 현재 끊어졌습니다. 새 URL 인 codeproject.com/Articles/1064964/…를
프라이 심슨

36

좋아 ... 나는 그들이 다르다는 의견을 냈지만 정확히 어떻게 알 수는 없지만 지금은 알고 있습니다.

String Property { get; } = "value";

와 같지 않다

String Property => "value";

차이점은 다음과 같습니다.

자동 초기화 프로그램을 사용하면 속성이 값의 인스턴스를 생성하고 해당 값을 지속적으로 사용합니다. 위의 게시물에는 Bill Wagner에 대한 깨진 링크가 있습니다. 이는 잘 설명하고 있으며, 본인이 이해하기 위해 올바른 링크를 검색했습니다.

내 상황에서는 ViewModel에서 View에 대한 명령을 자동으로 초기화하는 속성이 있습니다. 표현식 본문 이니셜 라이저를 사용하도록 속성을 변경하고 CanExecute 명령이 작동을 멈췄습니다.

여기에 모습이 있고 여기에 무슨 일이 있었는지입니다.

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

여기 내가 바꿨 던 것이 있습니다.

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

차이점은 내가 사용할 때입니다. { get; } = 은 해당 속성에서 SAME 명령을 만들고 참조 입니다. 내가 사용할 때 =>실제로 새 명령을 만들고 속성이 호출 될 때마다 반환합니다. 따라서 CanExecute항상 해당 명령의 새 참조를 업데이트하도록 지시했기 때문에 명령을 on으로 업데이트 할 수 없었 습니다.

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

모든 말은, 당신이 단지 백업 필드를 가리키고 있다면 잘 작동합니다. 이것은 자동 또는 표현식 본문이 반환 값을 생성 할 때만 발생합니다.


8
=> 구문은 get {return new Command (); } 구문.
Mafii

35

이것은 람다와 같은 함수를 사용하여 getter 전용 속성을 정의 할 수있는 표현식 본문 멤버라고하는 C # 6의 새로운 기능입니다.

다음과 같은 경우 구문 설탕 으로 간주되지만 동일한 IL을 생성 하지 못할 수 있습니다 .

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

위의 두 버전을 모두 컴파일하고 각각에 대해 생성 된 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

다음은 이름이 지정된 클래스에 정의 된 표현식 본문 멤버 버전의 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

참조 https://msdn.microsoft.com/en-us/magazine/dn802602.aspx를C # 6의이 기능 및 기타 새로운 기능에 대한 자세한 내용 를 .

이 게시물 C # 3.0+의 속성과 필드의 차이점을 참조하십시오. 에서 필드와 속성 게터의 .

최신 정보:

C # 7.0에는 식 본문 멤버가 속성, 생성자, 종료 자 및 인덱서를 포함하도록 확장되었습니다.


16

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

주로 상용구를 단축 할 수 있습니다.


7

C # 6을 사용하는 경우 다른 중요한 점이 있습니다.

'=>'대신 '수'사용할 수 있습니다 및입니다 '에서만 얻을'메소드 - 그것은 '세트'와 함께 사용할 수 없습니다.

C # 7의 경우 아래 @avenmore의 주석을 참조하십시오. 이제 더 많은 곳에서 사용할 수 있습니다. 여기 좋은 참조가 있습니다-https: //csharp.christiannagel.com/2017/01/25/expressionbodiedmembers/


8
C # 7을 사용하는 경우 더 이상 사실이 아닙니다. "C # 7.0은 생산성 향상을 계속합니다. C # 6을 사용하여 식 본문 멤버를 메서드 및 속성에 사용할 수있게되었으므로 이제 생성자, 소멸자, 속성 접근 자 및 이벤트 접근 자와 함께 사용할 수 있습니다. 게다가." ( 출처 )
avenmore

1

공유 다음 문을 들어 알렉스 부커 에서 자신의 대답

컴파일러가 식 본문 속성 멤버를 만나면 본질적으로 다음과 같이 getter로 변환합니다.

다음 스크린 샷을 참조하십시오 ( SharpLab 링크 사용 ).

public string APIBasePath => Configuration.ToolsAPIBasePath;

로 변환하다

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

스크린 샷 : 여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.