प्रॉपर्टी सिग्नेचर में C # में => असाइनमेंट क्या है


229

मैंने कहा कि कुछ कोड भर आया

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

अब मैं लम्बदा के भावों से कुछ परिचित हूँ। मैंने अभी तक इसे इस तरह से इस्तेमाल नहीं देखा है।

उपरोक्त कथन और क्या अंतर होगा

public int MaxHealth  = x ? y:z;

4
पहला ब्लॉक संपत्ति है दूसरा एक चर है
एमकेज़म अखगीरी

14
@ M.kazemAkhgary * एक क्षेत्र, एक चर नहीं।
मागी

जवाबों:


376

आप जो देख रहे हैं, वह एक अभिव्यक्ति-सदस्य है जो लंबोदर अभिव्यक्ति नहीं है।

जब कंपाइलर एक अभिव्यक्ति-संपन्न संपत्ति सदस्य का सामना करता है , तो यह अनिवार्य रूप से इसे इस तरह एक गेटर में बदल देता है:

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;

कौन से - यदि आप गुणों को समझते हैं - स्पष्ट होना चाहिए।

बस स्पष्ट होने के लिए, हालांकि: पहली सूची हुड के तहत एक गेट्टर के साथ एक संपत्ति है जिसे हर बार इसे एक्सेस करने के लिए कहा जाएगा। दूसरी लिस्टिंग एक फ़ील्ड इनिशियलाइज़र वाला फ़ील्ड है, जिसकी अभिव्यक्ति का मूल्यांकन केवल एक बार किया जाता है, जब टाइप तत्काल होता है।

वाक्यविन्यास में यह अंतर वास्तव में काफी सूक्ष्म है और इससे "गैचा" हो सकता है, जिसे "एसी # 6 गोचा: इनिशियलाइज़ेशन बनाम एक्सप्रेशन बोडिड मेंबर्स" शीर्षक वाले बिल वाग्नेर द्वारा वर्णित किया गया है ।

जबकि अभिव्यक्ति-ग्रस्त सदस्य लंबोदर अभिव्यक्ति हैं- जैसे , वे लंबोदर अभिव्यक्ति नहीं हैं । मूलभूत अंतर यह है कि एक लंबोदर अभिव्यक्ति या तो एक प्रतिनिधि उदाहरण या एक अभिव्यक्ति पेड़ के रूप में परिणत होती है। एक्सप्रेशन-बॉडी वाले सदस्य कंपाइलर को पर्दे के पीछे एक संपत्ति उत्पन्न करने के लिए एक निर्देश हैं। समानता (अधिक या कम) तीर के साथ शुरू होती है और समाप्त होती है ( =>)।

मैं यह भी जोड़ूंगा कि अभिव्यक्ति-योग्य सदस्य संपत्ति सदस्यों तक सीमित नहीं हैं। वे इन सभी सदस्यों पर काम करते हैं:

  • गुण
  • indexers
  • तरीके
  • ऑपरेटर्स

C # 7.0 में जोड़ा गया

हालाँकि, वे इन सदस्यों पर काम नहीं करते हैं:

  • नेस्टेड प्रकार
  • आयोजन
  • खेत

6
C # 7 के रूप में, कन्स्ट्रक्टर और फाइनल भी समर्थित हैं। docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
bzier

8
@bzier यह हमें कार्यात्मक प्रोग्रामर बनाने के लिए एक साजिश है। अगर हमेशा के लिए ईएलएसई !!
प्रहरी

सुपर कमाल का जवाब!
जैमे अरोयो गार्सिया

2
वर्तमान में बिल वैगनर के पोस्ट का लिंक टूट गया है। मुझे लगता है कि मुझे नया यूआरएल मिला: codeproject.com/Articles/1064964/…
फ्राई सिम्पसन

36

ठीक है ... मैंने एक टिप्पणी की थी कि वे अलग थे, लेकिन वास्तव में यह नहीं बता सकते थे कि अब मैं कैसे जानता हूं।

String Property { get; } = "value";

के रूप में ही नहीं है

String Property => "value";

यहाँ अंतर है ...

जब आप ऑटो इनिशियलाइज़र का उपयोग करते हैं तो संपत्ति मूल्य का उदाहरण बनाती है और उस मूल्य का लगातार उपयोग करती है। उपरोक्त पोस्ट में बिल वैगनर की एक टूटी हुई कड़ी है, जो इसे अच्छी तरह से समझाता है, और मैंने इसे स्वयं समझने के लिए सही लिंक खोजा।

मेरी स्थिति में मेरे पास मेरा ऑटो एक दृश्य के लिए एक ViewModel में एक कमांड को ऑटो करता था। मैंने अभिव्यक्ति बॉडीलाइज़र का उपयोग करने के लिए संपत्ति को बदल दिया और कमांड CanExecute ने काम करना बंद कर दिया।

यहाँ यह कैसा लग रहा था और यहाँ क्या हो रहा था।

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

यहाँ मैंने इसे बदल दिया है।

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

यहां अंतर यह है कि जब मैं उपयोग करता { get; } =हूं तो मैं उस संपत्ति में एसएएमई कमांड बनाता हूं और संदर्भ देता हूं। जब मैं उपयोग करता =>हूं तो मैं वास्तव में एक नई कमांड बनाता हूं और इसे हर बार वापस लौटाता हूं, जिसे संपत्ति कहा जाता है। इसलिए, मैं कभी भी CanExecuteअपने कमांड पर अपडेट नहीं कर सकता था क्योंकि मैं हमेशा उस कमांड के नए संदर्भ को अपडेट करने के लिए कह रहा था।

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

सभी ने कहा, यदि आप केवल एक बैकिंग क्षेत्र की ओर इशारा कर रहे हैं तो यह ठीक काम करता है। यह केवल तब होता है जब ऑटो या एक्सप्रेशन बॉडी रिटर्न वैल्यू बनाता है।


8
=> वाक्यविन्यास प्राप्त {वापसी नई कमांड () के बराबर है; } वाक्य - विन्यास।
माफिया

35

यह C # 6 की एक नई विशेषता है जिसे एक अभिव्यक्ति शारीरिक सदस्य कहा जाता है जो आपको एक लंबर को केवल एक लंबोदा फ़ंक्शन का उपयोग करके परिभाषित करने की अनुमति देता है।

हालांकि, इसे निम्नलिखित के लिए सिंटैक्टिक चीनी माना जाता है , वे समान आईएल का उत्पादन नहीं कर सकते हैं :

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

ऐसा लगता है कि यदि आप ऊपर के दोनों संस्करणों संकलन और प्रत्येक के लिए उत्पन्न आईएल तुलना आपको लगता है कि वे कर रहे हैं देखेंगे लगभग एक ही।

इस उत्तर में क्लासिक संस्करण के लिए आईएल है जब एक वर्ग में परिभाषित किया गया है 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

इस बारे में अधिक जानकारी और C # 6 में अन्य नई सुविधाओं के लिए https://msdn.microsoft.com/en-us/magazine/dn802602.aspx देखें ।

इस पोस्ट को देखें C # 3.0+ में प्रॉपर्टी और फील्ड के बीच अंतर एक फील्ड और C # में प्रॉपर्टी गेटर के बीच का अंतर।

अपडेट करें:

ध्यान दें कि अभिव्यक्ति-बॉडी वाले सदस्यों को C # 7.0 में गुण, निर्माता, फाइनल और इंडेक्सर्स शामिल करने के लिए विस्तारित किया गया था।


16

इसे एक्सप्रेशन बोडीड मेंबर कहा जाता है और इसे 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

उनके उत्तर में एलेक्स बुकर द्वारा साझा किए गए निम्न कथन के लिए

जब कंपाइलर एक अभिव्यक्ति-ग्रस्त संपत्ति के सदस्य का सामना करता है, तो यह अनिवार्य रूप से इसे इस तरह एक गेटर में बदल देता है:

कृपया निम्न स्क्रीनशॉट देखें , यह दिखाता है कि यह कथन ( SharpLab लिंक का उपयोग करके )

public string APIBasePath => Configuration.ToolsAPIBasePath;

करने के लिए धर्मान्तरित

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

स्क्रीनशॉट: यहां छवि विवरण दर्ज करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.