Apa tugas => dalam C # dalam tanda tangan properti


229

Saya menemukan beberapa kode yang mengatakan

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

Sekarang saya agak akrab dengan ekspresi Lambda. Saya hanya belum melihatnya menggunakannya dengan cara ini.

Apa yang akan menjadi perbedaan antara pernyataan di atas dan

public int MaxHealth  = x ? y:z;

4
blok pertama adalah properti yang kedua adalah variabel
M.kazem Akhgary

14
@ M.kazemAkhgary * bidang, bukan variabel.
Mafii

Jawaban:


376

Apa yang Anda lihat adalah anggota bertubuh ekspresi bukan ekspresi lambda.

Ketika kompiler bertemu dengan anggota properti bertubuh ekspresi , pada dasarnya ia mengubahnya menjadi pengambil seperti ini:

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

(Anda dapat memverifikasi ini sendiri dengan memompa kode ke alat yang disebut TryRoslyn .)

Anggota bertubuh ekspresi - seperti kebanyakan fitur C # 6 - hanyalah gula sintaksis . Ini berarti bahwa mereka tidak menyediakan fungsionalitas yang tidak dapat dicapai melalui fitur yang ada. Sebaliknya, fitur-fitur baru ini memungkinkan sintaksis yang lebih ekspresif dan ringkas untuk digunakan

Seperti yang Anda lihat, anggota yang memiliki ekspresi tubuh memiliki beberapa pintasan yang membuat anggota properti lebih kompak:

  • Tidak perlu menggunakan returnpernyataan karena kompilator dapat menyimpulkan bahwa Anda ingin mengembalikan hasil ekspresi
  • Tidak perlu membuat blok pernyataan karena tubuh hanya satu ekspresi
  • Tidak perlu menggunakan getkata kunci karena tersirat oleh penggunaan sintaks anggota bertubuh ekspresi.

Saya telah membuat poin terakhir dengan berani karena relevan dengan pertanyaan Anda yang sebenarnya, yang akan saya jawab sekarang.

Perbedaan antara...

// expression-bodied member property
public int MaxHealth => x ? y:z;

Dan...

// field with field initializer
public int MaxHealth = x ? y:z;

Apakah sama dengan perbedaan antara ...

public int MaxHealth
{
    get
    {
        return x ? y:z;
    }
}

Dan...

public int MaxHealth = x ? y:z;

Yang - jika Anda memahami properti - harus jelas.

Hanya untuk menjadi jelas, daftar pertama adalah properti dengan pengambil di bawah kap yang akan dipanggil setiap kali Anda mengaksesnya. Daftar kedua adalah bidang dengan inisialisasi bidang, yang ekspresinya hanya dievaluasi satu kali, ketika jenisnya dipakai.

Perbedaan dalam sintaksis ini sebenarnya cukup halus dan dapat mengarah pada "gotcha" yang dijelaskan oleh Bill Wagner dalam posting berjudul "AC # 6 gotcha: Inisialisasi vs Anggota Ekspresi yang Bertubuh" .

Sementara anggota yang berekspresi adalah ekspresi seperti lambda , mereka bukan ekspresi lambda. Perbedaan mendasar adalah bahwa ekspresi lambda menghasilkan instance delegasi atau pohon ekspresi. Anggota bertubuh ekspresi hanyalah arahan ke kompiler untuk menghasilkan properti di belakang layar. Kesamaan (lebih atau kurang) dimulai dan diakhiri dengan panah ( =>).

Saya juga akan menambahkan bahwa anggota yang bertubuh ekspresi tidak terbatas pada anggota properti. Mereka mengerjakan semua anggota ini:

  • Properti
  • Pengindeks
  • Metode
  • Operator

Ditambahkan dalam C # 7.0

Namun, mereka tidak bekerja pada anggota ini:

  • Jenis bersarang
  • Acara
  • Bidang

6
Pada C # 7, konstruktor dan finalizer juga didukung. docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
bzier

8
@ Bzier Ini adalah konspirasi untuk membuat kita programmer fungsional. JIKA KEMUDIAN LAIN selamanya !!
Sentinel

Jawaban super luar biasa!
Jaime Arroyo Garcia

2
Tautan ke pos Bill Wagner saat ini rusak. Saya rasa saya menemukan url baru: codeproject.com/Articles/1064964/...
Fry Simpson

36

Ok ... Saya berkomentar bahwa mereka berbeda tetapi tidak bisa menjelaskan dengan pasti bagaimana tetapi sekarang saya tahu.

String Property { get; } = "value";

tidak sama dengan

String Property => "value";

Inilah bedanya ...

Ketika Anda menggunakan penginisialisasi otomatis, properti menciptakan instance nilai dan menggunakan nilai itu secara terus-menerus. Dalam posting di atas ada tautan yang rusak ke Bill Wagner, yang menjelaskan hal ini dengan baik, dan saya mencari tautan yang benar untuk memahaminya sendiri.

Dalam situasi saya, properti saya otomatis menginisialisasi perintah dalam ViewModel untuk Tampilan. Saya mengubah properti untuk menggunakan initializer bodied ekspresi dan perintah CanExecute berhenti bekerja.

Begini tampilannya dan inilah yang terjadi.

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

inilah yang saya ubah.

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

Perbedaannya di sini adalah ketika saya menggunakan { get; } =saya membuat dan referensi perintah SAMA di properti itu. Ketika saya menggunakan, =>saya benar-benar membuat perintah baru dan mengembalikannya setiap kali properti dipanggil. Oleh karena itu, saya tidak pernah dapat memperbarui CanExecuteperintah saya karena saya selalu mengatakannya untuk memperbarui referensi baru dari perintah itu.

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

Semua yang dikatakan, jika Anda hanya menunjuk ke bidang dukungan maka itu berfungsi dengan baik. Ini hanya terjadi ketika badan ekspresi atau otomatis membuat nilai kembali.


8
Sintaks => sama dengan get {return Command baru (); } sintaks.
Mafii

35

Ini adalah fitur baru C # 6 yang disebut anggota bertubuh ekspresi yang memungkinkan Anda untuk mendefinisikan properti pengambil hanya menggunakan fungsi seperti lambda.

Meskipun dianggap sebagai sintaksis gula untuk yang berikut, mereka mungkin tidak menghasilkan IL identik:

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

Ternyata jika Anda mengkompilasi kedua versi di atas dan membandingkan IL yang dihasilkan untuk masing-masing Anda akan melihat bahwa mereka hampir sama.

Berikut adalah IL untuk versi klasik dalam jawaban ini ketika didefinisikan dalam kelas bernama 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

Dan di sini adalah IL untuk versi anggota bertubuh ekspresi ketika didefinisikan dalam kelas bernama 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

Lihat https://msdn.microsoft.com/en-us/magazine/dn802602.aspx untuk informasi lebih lanjut tentang ini dan fitur baru lainnya di C # 6.

Lihat posting ini Perbedaan antara Properti dan Bidang di C # 3.0+ pada perbedaan antara bidang dan pengambil properti di C #.

Memperbarui:

Perhatikan bahwa anggota yang berekspresi diperluas untuk memasukkan properti, konstruktor, finalizer dan pengindeks dalam C # 7.0.


16

Ini disebut Expression Bodied Member dan diperkenalkan di C # 6. Ini hanyalah gula sintaksis di atas satu- getsatunya properti.

Itu sama dengan:

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

Setara dengan deklarasi metode tersedia:

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

Terutama memungkinkan Anda memendekkan boilerplate.


7

Satu hal penting lainnya jika Anda menggunakan C # 6:

'=>' dapat digunakan sebagai ganti 'get' dan hanya untuk metode 'get only' - ini tidak dapat digunakan dengan 'set'.

Untuk C # 7, lihat komentar dari @avenmore di bawah - sekarang dapat digunakan di lebih banyak tempat. Inilah referensi yang bagus - https://csharp.christiannagel.com/2017/01/25/expressionbodiedmembers/


8
Tidak lagi benar jika Anda menggunakan C # 7. "C # 7.0 berlanjut dengan peningkatan produktivitas. Anggota bertubuh ekspresi telah tersedia dengan C # 6 untuk metode dan properti, sekarang mereka dapat digunakan dengan konstruktor, destruktor, pengakses properti, dan pengakses acara demikian juga." ( Sumber )
avenmore

1

Untuk pernyataan berikut dibagikan oleh Alex Booker dalam jawaban mereka

Ketika kompiler bertemu dengan anggota properti bertubuh ekspresi, pada dasarnya ia mengubahnya menjadi pengambil seperti ini:

Silakan lihat screenshot berikut , ini menunjukkan bagaimana pernyataan ini (menggunakan tautan SharpLab )

public string APIBasePath => Configuration.ToolsAPIBasePath;

dikonversi ke

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

Tangkapan layar: masukkan deskripsi gambar di sini

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.