Bir özellik imzasındaki C #'daki => ataması nedir


229

Bazı kodlarla karşılaştım

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

Şimdi Lambda ifadelerine biraz aşinayım. Sadece bu şekilde kullandığını görmedim.

Yukarıdaki ifade ile

public int MaxHealth  = x ? y:z;

4
ilk blok mülk ikincisi değişken
M.kazem Akhgary

14
@ M.kazemAkhgary * bir alan, değişken değil.
Mafii

Yanıtlar:


377

Baktığınız şey lambda ifadesi değil , ifade gövdeli bir üyedir .

Derleyici, ifade gövdeli bir özellik üyesiyle karşılaştığında, bunu esas olarak şöyle bir alıcıya dönüştürür:

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

(Kodu TryRoslyn adlı bir araca pompalayarak bunu kendiniz doğrulayabilirsiniz .)

İfade gövdeli üyeler - çoğu C # 6 özelliği gibi - sadece sözdizimsel şekerdir . Bu, mevcut özellikler yoluyla elde edilemeyen işlevler sağlamadığı anlamına gelir. Bunun yerine, bu yeni özellikler daha anlamlı ve özlü bir sözdiziminin kullanılmasına izin verir

Gördüğünüz gibi, ifade gövdeli üyelerin mülk üyelerini daha kompakt hale getiren birkaç kısayolu vardır:

  • Bir returnifade kullanmaya gerek yoktur, çünkü derleyici ifadenin sonucunu döndürmek istediğinizi çıkarabilir
  • Bir ifade bloğu oluşturmaya gerek yoktur çünkü gövde yalnızca bir ifadedir
  • getAnahtar kelimeyi kullanmaya gerek yoktur, çünkü ifade gövdeli üye sözdiziminin kullanımı ile ima edilir.

Son noktayı kalınlaştırdım, çünkü şimdi cevaplayacağım asıl sorunuzla ilgili.

Arasındaki fark...

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

Ve...

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

Aradaki farkla aynı ...

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

Ve...

public int MaxHealth = x ? y:z;

Hangi - özellikleri anlarsanız - açık olmalıdır.

Ancak açık olmak gerekirse: ilk liste, her eriştiğinizde çağrılacak kaputun altında bir alıcıya sahip bir özelliktir. İkinci liste, alanı başlatırken ifadesi yalnızca bir kez değerlendirilen bir alan başlatıcısı olan bir alandır.

Sözdizimindeki bu fark aslında oldukça incedir ve Bill Wagner tarafından "AC # 6 gotcha: Başlatma ve İfade Bodied Üyeleri" başlıklı bir yazıda açıklanan "gotcha" ya yol açabilir. .

İfadesi vücutlu üyeleri lambda anlatım- olmakla gibi , bunlar değil lambda ifadeleri. Temel fark lambda ifadesinin bir delege örneği veya bir ifade ağacı ile sonuçlanmasıdır. İfade gövdeli üyeler, sadece sahne arkasında bir özellik oluşturmak için derleyiciye bir direktiftir. Benzerlik (az ya da çok) ok ( =>) ile başlar ve biter .

Ayrıca ifade gövdeli üyelerin mülk üyeleriyle sınırlı olmadığını da ekleyeceğim. Tüm bu üyeler üzerinde çalışıyorlar:

  • Özellikleri
  • Dizinleyiciler
  • Yöntemler
  • Operatörler

Eklendi C # 7.0

Ancak, bu üyeler üzerinde çalışmazlar:

  • İç İçe Tipler
  • Etkinlikler
  • Alanlar

6
C # 7'den itibaren yapıcılar ve sonlandırıcılar da desteklenmektedir. docs.microsoft.com/tr-tr/dotnet/csharp/programming-guide/…
bzier

8
@bzier Bizi işlevsel programcılar yapmak bir komplo. Sonsuza dek başka olursa !!
Sentinel

Süper harika cevap!
Jaime Arroyo Garcia

2
Bill Wagner'in gönderisine bağlantı şu anda bozuk. Sanırım yeni url'yi buldum: codeproject.com/Articles/1064964/…
Fry Simpson

36

Tamam ... Farklı olduklarına dair bir yorum yaptım ama nasıl olduğunu tam olarak açıklayamadım ama şimdi biliyorum.

String Property { get; } = "value";

ile aynı değil

String Property => "value";

İşte fark ...

Otomatik başlatıcıyı kullandığınızda özellik, değer örneğini oluşturur ve bu değeri kalıcı olarak kullanır. Yukarıdaki yazıda Bill Wagner'e bunu açıklayan kırık bir bağlantı var ve bunu kendim anlamak için doğru bağlantıyı aradım.

Benim durumumda bir görünüm için ViewModel bir komutu otomatik başlatmak benim özellik vardı. Ifade gövdeli başlatıcısı kullanmak için özelliği değiştirdi ve CanExecute komutu çalışmayı durdurdu.

İşte neye benzediğini ve neler olduğunu işte burada.

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

İşte bunu değiştirdim.

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

Buradaki fark { get; } =, bu özellikteki SAME komutunu oluşturup başvuru yaptığım zamandır. Ben kullandığımda =>aslında yeni bir komut oluşturmak ve özellik her çağrıldığında geri. Bu nedenle, komutumda hiçbir zaman güncelleme yapamadım CanExecuteçünkü her zaman komutun yeni bir başvurusunu güncellemesini söylüyordum.

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

Tüm bunlar, sadece bir destek alanına işaret ediyorsanız iyi çalışıyor. Bu yalnızca otomatik veya ifade gövdesi dönüş değeri oluşturduğunda gerçekleşir.


8
=> Sözdizimi get {return new Command (); sözdizimi.
Mafii

35

Bu, lambda benzeri bir işlev kullanarak yalnızca bir alıcı özelliği tanımlamanızı sağlayan, ifade gövdeli üye adı verilen yeni bir C # 6 özelliğidir.

Bu kabul edilirken sözdizimsel şeker aşağıdakiler için, bunlar olmayabilir özdeş IL üretmek:

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

Yukarıdakilerin her iki versiyonunu da derlerseniz ve her biri için üretilen IL'yi karşılaştırırsanız , bunların hemen hemen aynı olduklarını göreceksiniz .

İşte bu sınıftaki bir sınıfta tanımlandığında klasik yanıtın IL'si 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

Ve işte, şu adlı bir sınıfta tanımlandığında gövdeli üye sürümü ifadesinin IL'si 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

Bu ve C # 6'daki diğer yeni özellikler hakkında daha fazla bilgi için https://msdn.microsoft.com/tr-tr/magazine/dn802602.aspx adresine bakın .

Bu yazı Bkz C # 3.0 + Mülkiyet ve Alan arasındaki Fark bir alan ve C # bir özellik gaz giderici arasındaki farka.

Güncelleme:

İfade gövdeli üyelerin C # 7.0'daki özellikleri, yapıcıları, sonlandırıcıları ve dizinleyicileri içerecek şekilde genişletildiğini unutmayın.


16

Buna İfade Bodied Üyesi denir ve C # 6'da tanıtıldı.get . Tek bir mülk .

Şuna eşittir:

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

Yöntem beyanının eşdeğeri kullanılabilir:

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

Temel olarak kazan plakasını kısaltmanıza izin verir.


7

C # 6 kullanıyorsanız bir diğer önemli nokta:

'=>', 'get' yerine kullanılabilir ve yalnızca 'get get' yöntemleri içindir - 'set' ile kullanılamaz.

C # 7 için, aşağıdaki @ avenmore'un yorumuna bakın - artık daha fazla yerde kullanılabilir. İşte iyi bir referans - https://csharp.christiannagel.com/2017/01/25/expressionbodiedmembers/


8
C # 7 kullanıyorsanız artık doğru değil. "C # 7.0 üretkenlik geliştirmeleriyle devam ediyor. İfade gövdeli üyeler yöntemler ve özellikler için C # 6 ile kullanılabilir, şimdi yapıcılar, yıkıcılar, özellik erişimcileri ve olay erişimcileri ile kullanılabilir de. " ( Kaynak )
avenmore

1

Tarafından paylaşılan aşağıdaki deyimi için Alex Booker içinde verdikleri yanıta

Derleyici, ifade gövdeli bir özellik üyesiyle karşılaştığında, bunu esas olarak şöyle bir alıcıya dönüştürür:

Lütfen aşağıdaki ekran görüntüsüne bakın, bu ifadenin nasıl olduğunu gösterir ( SharpLab bağlantısını kullanarak )

public string APIBasePath => Configuration.ToolsAPIBasePath;

dönüşür

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

Ekran görüntüsü: resim açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.