System.ValueTuple और System.Tuple में क्या अंतर है?


139

मैंने कुछ C # 7 लाइब्रेरी को विघटित किया और देखा कि ValueTupleजेनरिक का उपयोग किया जा रहा है। इसके बजाय क्या ValueTuplesऔर क्यों नहीं Tupleहैं?


मुझे लगता है कि इसका उल्लेख डॉट एनईटी टपल क्लास से है। क्या आप कृपया एक नमूना कोड साझा कर सकते हैं। ताकि समझने में आसानी हो।
Ranadip दत्ता

14
@ रानदीप दत्ता: यदि आप जानते हैं कि एक ट्यूपल क्या है, तो आपको प्रश्न को समझने के लिए नमूना कोड की आवश्यकता नहीं है। सवाल ही सीधा है: क्या है ValueTuple और यह Tuple से कैसे भिन्न है?
बोल्टलॉक

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

2
जब रोसलिन के लिए सोर्स कोड जीथब पर उपलब्ध है तो आप इन्हें क्यों नष्ट कर देते हैं?
ज़ीन मक्की

@ user3185569 शायद इसलिए क्योंकि F12 ऑटो-डिकंपाइल चीजें और
गीथहब में

जवाबों:


203

इसके बजाय क्या ValueTuplesऔर क्यों नहीं Tupleहैं?

ValueTupleएक संरचना है जो मूल System.Tupleवर्ग के समान एक टपल को दर्शाता है ।

के बीच मुख्य अंतर हैं Tupleऔर ValueTupleहैं:

  • System.ValueTupleएक मान प्रकार (संरचना) है, जबकि System.Tupleएक संदर्भ प्रकार ( class) है। आवंटन और जीसी दबाव के बारे में बात करते समय यह सार्थक है।
  • System.ValueTupleकेवल एक ही नहीं है struct, यह एक परिवर्तनशील है, और एक का उपयोग करते समय उन्हें सावधान रहना होगा। सोचिये जब कोई वर्ग System.ValueTupleएक क्षेत्र के रूप में धारण करता है तो क्या होता है ।
  • System.ValueTuple गुणों के बजाय खेतों के माध्यम से इसकी वस्तुओं को उजागर करता है।

सी # 7 तक, टुपल्स का उपयोग करना बहुत सुविधाजनक नहीं था। अपने क्षेत्र के नाम हैं Item1, Item2अधिकांश अन्य भाषाओं की तरह उनके लिए आदि, और भाषा की आपूर्ति नहीं की थी वाक्य रचना चीनी करते हैं (अजगर, स्काला)।

जब .NET भाषा डिज़ाइन टीम ने ट्यूपल्स को शामिल करने और भाषा स्तर पर उनमें सिंटेक्स शुगर जोड़ने का निर्णय लिया, तो एक महत्वपूर्ण कारक प्रदर्शन था। साथ ValueTupleएक मान प्रकार किया जा रहा है, तो आप जी सी दबाव जब उन्हें प्रयोग क्योंकि (एक कार्यान्वयन विस्तार के रूप में) से बच सकते हैं वे ढेर पर आवंटित किया जाएगा।

इसके अतिरिक्त, structरनटाइम द्वारा एक स्वचालित (उथले) समानता शब्दार्थ प्राप्त करता है, जहां एक classनहीं है। हालांकि डिजाइन टीम ने सुनिश्चित किया कि टुपल्स के लिए और भी अधिक अनुकूलित समानता होगी, इसलिए इसके लिए एक कस्टम समानता को लागू किया।

यहाँ डिजाइन नोट्सTuples से एक पैराग्राफ है :

संरचना या कक्षा:

जैसा कि उल्लेख किया गया है, मैं structsइसके बजाय तुच्छ प्रकार बनाने का प्रस्ताव करता हूं classes, ताकि उनके साथ कोई आवंटन जुर्माना न जुड़ा हो। उन्हें जितना संभव हो उतना हल्का होना चाहिए।

तर्क से, structsअधिक महंगा होने का अंत हो सकता है, क्योंकि असाइनमेंट एक बड़े मूल्य को कॉपी करता है। इसलिए अगर उन्हें पैदा किए जाने से बहुत अधिक सौंपा जाता है, तो structsएक बुरा विकल्प होगा।

अपने बहुत ही प्रेरणा में, हालांकि, ट्यूपेल अल्पकालिक हैं। आप इनका उपयोग तब करेंगे जब पुर्जे पूरे से अधिक महत्वपूर्ण हों। इसलिए सामान्य पैटर्न का निर्माण करना होगा, वापस लौटना होगा और तुरंत उन्हें विघटित करना होगा। इस स्थिति में संरचनाएं स्पष्ट रूप से बेहतर हैं।

संरचनाओं में कई अन्य लाभ भी हैं, जो निम्नलिखित में स्पष्ट हो जाएंगे।

उदाहरण:

आप आसानी से देख सकते हैं कि साथ काम करना System.Tupleबहुत जल्दी अस्पष्ट हो जाता है। उदाहरण के लिए, मान लें कि हमारे पास एक विधि है जो एक राशि और एक गणना की गणना करती है List<Int>:

public Tuple<int, int> DoStuff(IEnumerable<int> values)
{
    var sum = 0;
    var count = 0;

    foreach (var value in values) { sum += value; count++; }

    return new Tuple(sum, count);
}

प्राप्त होने पर, हम समाप्त करते हैं:

Tuple<int, int> result = DoStuff(Enumerable.Range(0, 10));

// What is Item1 and what is Item2?
// Which one is the sum and which is the count?
Console.WriteLine(result.Item1);
Console.WriteLine(result.Item2);

जिस तरह से आप नामांकित तर्कों में मूल्य ट्यूपल्स को डिक्रिप्ट कर सकते हैं, वह फीचर की वास्तविक शक्ति है:

public (int sum, int count) DoStuff(IEnumerable<int> values) 
{
    var res = (sum: 0, count: 0);
    foreach (var value in values) { res.sum += value; res.count++; }
    return res;
}

और प्राप्त अंत पर:

var result = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {result.Sum}, Count: {result.Count}");

या:

var (sum, count) = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {sum}, Count: {count}");

संकलक उपहार:

यदि हम अपने पिछले उदाहरण के कवर के नीचे देखते हैं, तो हम यह देख सकते हैं कि कंपाइलर कैसे व्याख्या कर रहा है ValueTupleजब हम इसे डिकोन करने के लिए कहते हैं:

[return: TupleElementNames(new string[] {
    "sum",
    "count"
})]
public ValueTuple<int, int> DoStuff(IEnumerable<int> values)
{
    ValueTuple<int, int> result;
    result..ctor(0, 0);
    foreach (int current in values)
    {
        result.Item1 += current;
        result.Item2++;
    }
    return result;
}

public void Foo()
{
    ValueTuple<int, int> expr_0E = this.DoStuff(Enumerable.Range(0, 10));
    int item = expr_0E.Item1;
    int arg_1A_0 = expr_0E.Item2;
}

आंतरिक रूप से, संकलित कोड उपयोग करता है Item1और Item2, लेकिन यह सब हमसे दूर है क्योंकि हम एक विघटित कूप के साथ काम करते हैं। नाम के तर्कों के साथ एक tuple के साथ एनोटेट हो जाता है TupleElementNamesAttribute। यदि हम विघटित होने के बजाय एक ही ताजा चर का उपयोग करते हैं, तो हमें यह मिलता है:

public void Foo()
{
    ValueTuple<int, int> valueTuple = this.DoStuff(Enumerable.Range(0, 10));
    Console.WriteLine(string.Format("Sum: {0}, Count: {1})", valueTuple.Item1, valueTuple.Item2));
}

नोट संकलक अभी भी कुछ जादू होता है (विशेषता के माध्यम से) जब हम अपने आवेदन डिबग, के रूप में यह देखने के लिए अजीब होगा बनाने के लिए है कि Item1, Item2


1
ध्यान दें कि आप सरल (और मेरी राय में, बेहतर) सिंटैक्स का उपयोग कर सकते हैंvar (sum, count) = DoStuff(Enumerable.Range(0, 10));
Abion47

@ Abion47 यदि दोनों प्रकार भिन्न होते हैं तो क्या होगा?
युवल इट्ज़चकोव

इसके अलावा कैसे अपने अंक करते हैं "यह एक है परिवर्तनशील struct" और "यह उजागर करता है केवल पढ़ने के लिए फ़ील्ड" इस बात से सहमत?
कोडइन्चोस

@CodesInChaos यह नहीं करता है। मैंने देखा [यह] ( github.com/dotnet/corefx/blob/master/src/Common/src/System/… ), लेकिन मुझे नहीं लगता कि संकलक द्वारा उत्सर्जित किया जा रहा अंत क्या है, क्योंकि स्थानीय क्षेत्र नहीं हो सकते हैं वैसे भी। मुझे लगता है कि प्रस्ताव का मतलब था "यदि आप चाहें तो आप उन्हें आसानी से बना सकते हैं, लेकिन यह आपके ऊपर है" , जिसका मैंने गलत अर्थ निकाला।
युवल इट्ज़चकोव

1
कुछ एनआईटी: "उन्हें स्टैक पर आवंटित किया जाएगा" - केवल स्थानीय चर के लिए सच है। इसमें कोई संदेह नहीं है कि आप इसे जानते हैं, लेकिन दुर्भाग्य से, जिस तरह से आपने इसे दोहराया है, उस मिथक को बनाए रखने की संभावना है कि मूल्य प्रकार हमेशा स्टैक में रहते हैं।
पीटर डनिहो

26

के बीच का अंतर Tupleऔर ValueTupleवह यह है कि Tupleएक संदर्भ प्रकार है और ValueTupleएक मान प्रकार है। उत्तरार्द्ध वांछनीय है क्योंकि C # 7 में भाषा में परिवर्तन से ट्यूपल्स का अधिक बार उपयोग किया जा रहा है, लेकिन प्रत्येक टपल के लिए ढेर पर एक नई वस्तु आवंटित करना एक चिंता का विषय है, खासकर जब यह अनावश्यक है।

हालाँकि, C # 7 में, विचार यह है कि ट्यूपर के उपयोग के लिए सिंटैक्स चीनी को जोड़ने के कारण आपको कभी भी स्पष्ट रूप से टाइप करने की आवश्यकता नहीं है । उदाहरण के लिए, C # 6 में, यदि आप किसी मान को वापस करने के लिए टपल का उपयोग करना चाहते हैं, तो आपको निम्नलिखित कार्य करने होंगे:

public Tuple<string, int> GetValues()
{
    // ...
    return new Tuple(stringVal, intVal);
}

var value = GetValues();
string s = value.Item1; 

हालाँकि, C # 7 में, आप इसका उपयोग कर सकते हैं:

public (string, int) GetValues()
{
    // ...
    return (stringVal, intVal);
}

var value = GetValues();
string s = value.Item1; 

तुम भी एक कदम आगे जा सकते हैं और मूल्यों के नाम दे सकते हैं:

public (string S, int I) GetValues()
{
    // ...
    return (stringVal, intVal);
}

var value = GetValues();
string s = value.S; 

... या पूरी तरह से ट्यूल को फिर से बनाना:

public (string S, int I) GetValues()
{
    // ...
    return (stringVal, intVal);
}

var (S, I) = GetValues();
string s = S;

Tuples को अक्सर C # प्री -7 में उपयोग नहीं किया जाता था क्योंकि वे बोझिल और वाचाल होते थे, और केवल उन मामलों में उपयोग किया जाता था, जहाँ काम के लिए केवल एक ही उदाहरण के लिए डेटा क्लास / स्ट्रक्चर का निर्माण करना उसके लायक होने से अधिक परेशानी होगी। लेकिन C # 7 में, टुपल्स को अब भाषा-स्तरीय समर्थन प्राप्त है, इसलिए उनका उपयोग करना ज्यादा साफ और अधिक उपयोगी है।


10

मैंने स्रोत को दोनों के लिए देखा Tupleऔर ValueTuple। अंतर यह है कि Tupleएक है classऔर ValueTupleएक है structकि लागू होता है IEquatable

इसका मतलब है कि Tuple == Tupleवापस आ जाएगीfalse अगर वे एक ही उदाहरण नहीं हैं, लेकिन ValueTuple == ValueTupleवापस आ जाएगी trueअगर वे एक ही प्रकार के और के हैं Equalsरिटर्न trueमूल्यों उनमें शामिल से प्रत्येक के लिए।


हालांकि यह उससे कहीं ज्यादा है।
BoltClock

2
@BoltClock आपकी टिप्पणी रचनात्मक होगी यदि आप विस्तार से बताएंगे
पीटर मॉरिस

3
इसके अलावा, मूल्य प्रकार जरूरी स्टैक पर नहीं जाते हैं। अंतर यह है कि जब भी उस चर को संग्रहीत किया जाता है, जो स्टैक हो सकता है या नहीं हो सकता है, तो संदर्भ के बजाय शब्दार्थ का प्रतिनिधित्व करते हैं।
१५

6

अन्य उत्तर महत्वपूर्ण बिंदुओं का उल्लेख करना भूल गए। रीफ़्रेशिंग के बाद, मैं स्रोत कोड से XML प्रलेखन का संदर्भ देने जा रहा हूं :

ValueTuple प्रकार (arity 0 से 8 तक) में रनटाइम कार्यान्वयन शामिल है जो C # में tuples और F # में संरचनात्मक tuples को रेखांकित करता है।

भाषा सिंटैक्स के माध्यम से बनाए जाने के अलावा , वे ValueTuple.Createकारखाने के तरीकों के माध्यम से सबसे आसानी से बनाए जाते हैं । उस System.ValueTupleप्रकार के प्रकार भिन्न होते हैं System.Tuple:

  • वे कक्षाओं के बजाय संरचनाएं हैं,
  • वे पठनीयता के बजाय परस्पर परिवर्तनशील हैं , और
  • उनके सदस्य (जैसे कि Item1, Item2, आदि) गुण के बजाय फ़ील्ड हैं।

इस प्रकार और C # 7.0 संकलक के परिचय के साथ, आप आसानी से लिख सकते हैं

(int, string) idAndName = (1, "John");

और एक विधि से दो मान लौटाएँ:

private (int, string) GetIdAndName()
{
   //.....
   return (id, name);
}

इसके विपरीत System.Tupleआप इसके सदस्यों (म्यूटेबल) को अपडेट कर सकते हैं क्योंकि वे सार्वजनिक रूप से पढ़े जाने वाले फील्ड हैं जिन्हें सार्थक नाम दिए जा सकते हैं:

(int id, string name) idAndName = (1, "John");
idAndName.name = "New Name";

"एरिटी 0 से 8"। आह, मुझे यह तथ्य पसंद है कि उनमें 0-टपल शामिल है। यह एक प्रकार के खाली प्रकार के रूप में इस्तेमाल किया जा सकता है, और जेनरिक में अनुमति दी जाएगी जब कुछ प्रकार के पैरामीटर की आवश्यकता नहीं होती है, जैसे कि class MyNonGenericType : MyGenericType<string, ValueTuple, int>आदि
जेपी स्टिग नीलसन

6

उपरोक्त टिप्पणियों के अलावा, ValueTuple का एक दुर्भाग्यपूर्ण गोथा है, मान प्रकार के रूप में, नामित तर्क आईएल के संकलित होने पर मिट जाते हैं, इसलिए वे क्रम में क्रमांकन के लिए उपलब्ध नहीं हैं।

यानी आपके मीठे नाम वाले तर्क अभी भी "आइटम 1", "आइटम 2" आदि के रूप में समाप्त हो जाएंगे, जब उदाहरण के लिए Json.NET के माध्यम से क्रमबद्ध।


2
तो तकनीकी रूप से है कि एक समानता और नहीं एक फर्क है;)
JAD

2

इन दोनों तथ्यों पर एक त्वरित स्पष्टीकरण जोड़ने के लिए देर से जुड़ना:

  • वे कक्षाओं के बजाय संरचनाएं हैं
  • वे पठनीयता के बजाय परस्पर परिवर्तनशील हैं

कोई सोचता है कि मूल्य-वृद्धियों को बदलना- मस्से सीधे होंगे:

 foreach (var x in listOfValueTuples) { x.Foo = 103; } // wont even compile because x is a value (struct) not a variable

 var d = listOfValueTuples[0].Foo;

कोई इसे इस तरह से हल करने की कोशिश कर सकता है:

 // initially *.Foo = 10 for all items
 listOfValueTuples.Select(x => x.Foo = 103);

 var d = listOfValueTuples[0].Foo; // 'd' should be 103 right? wrong! it is '10'

इस विचित्र व्यवहार का कारण यह है कि मूल्य-टुपल्स बिल्कुल मूल्य-आधारित (संरचनाएं) हैं और इस प्रकार .Select (...) कॉल, मूल के बजाय क्लोन-स्ट्रक्चर पर काम करता है। इसे हल करने के लिए हमें इसका सहारा लेना चाहिए:

 // initially *.Foo = 10 for all items
 listOfValueTuples = listOfValueTuples
     .Select(x => {
         x.Foo = 103;
         return x;
     })
     .ToList();

 var d = listOfValueTuples[0].Foo; // 'd' is now 103 indeed

वैकल्पिक रूप से एक सीधा दृष्टिकोण की कोशिश कर सकते हैं:

   for (var i = 0; i < listOfValueTuples.Length; i++) {
        listOfValueTuples[i].Foo = 103; //this works just fine

        // another alternative approach:
        //
        // var x = listOfValueTuples[i];
        // x.Foo = 103;
        // listOfValueTuples[i] = x; //<-- vital for this alternative approach to work   if you omit this changes wont be saved to the original list
   }

   var d = listOfValueTuples[0].Foo; // 'd' is now 103 indeed

आशा है कि यह किसी को सूची-होस्टेड मूल्य-ट्यूपल्स से बाहर पूंछ बनाने के लिए संघर्ष करने में मदद करता है।

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