मैंने कुछ C # 7 लाइब्रेरी को विघटित किया और देखा कि ValueTuple
जेनरिक का उपयोग किया जा रहा है। इसके बजाय क्या ValueTuples
और क्यों नहीं Tuple
हैं?
मैंने कुछ C # 7 लाइब्रेरी को विघटित किया और देखा कि ValueTuple
जेनरिक का उपयोग किया जा रहा है। इसके बजाय क्या ValueTuples
और क्यों नहीं Tuple
हैं?
जवाबों:
इसके बजाय क्या
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
।
var (sum, count) = DoStuff(Enumerable.Range(0, 10));
के बीच का अंतर 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 में, टुपल्स को अब भाषा-स्तरीय समर्थन प्राप्त है, इसलिए उनका उपयोग करना ज्यादा साफ और अधिक उपयोगी है।
मैंने स्रोत को दोनों के लिए देखा Tuple
और ValueTuple
। अंतर यह है कि Tuple
एक है class
और ValueTuple
एक है struct
कि लागू होता है IEquatable
।
इसका मतलब है कि Tuple == Tuple
वापस आ जाएगीfalse
अगर वे एक ही उदाहरण नहीं हैं, लेकिन ValueTuple == ValueTuple
वापस आ जाएगी true
अगर वे एक ही प्रकार के और के हैं Equals
रिटर्न true
मूल्यों उनमें शामिल से प्रत्येक के लिए।
अन्य उत्तर महत्वपूर्ण बिंदुओं का उल्लेख करना भूल गए। रीफ़्रेशिंग के बाद, मैं स्रोत कोड से XML प्रलेखन का संदर्भ देने जा रहा हूं :
ValueTuple प्रकार (arity 0 से 8 तक) में रनटाइम कार्यान्वयन शामिल है जो C # में tuples और F # में संरचनात्मक tuples को रेखांकित करता है।
भाषा सिंटैक्स के माध्यम से बनाए जाने के अलावा , वे ValueTuple.Create
कारखाने के तरीकों के माध्यम से सबसे आसानी से बनाए जाते हैं
। उस System.ValueTuple
प्रकार के प्रकार भिन्न होते हैं System.Tuple
:
इस प्रकार और 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";
class MyNonGenericType : MyGenericType<string, ValueTuple, int>
आदि
उपरोक्त टिप्पणियों के अलावा, ValueTuple का एक दुर्भाग्यपूर्ण गोथा है, मान प्रकार के रूप में, नामित तर्क आईएल के संकलित होने पर मिट जाते हैं, इसलिए वे क्रम में क्रमांकन के लिए उपलब्ध नहीं हैं।
यानी आपके मीठे नाम वाले तर्क अभी भी "आइटम 1", "आइटम 2" आदि के रूप में समाप्त हो जाएंगे, जब उदाहरण के लिए Json.NET के माध्यम से क्रमबद्ध।
इन दोनों तथ्यों पर एक त्वरित स्पष्टीकरण जोड़ने के लिए देर से जुड़ना:
कोई सोचता है कि मूल्य-वृद्धियों को बदलना- मस्से सीधे होंगे:
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
आशा है कि यह किसी को सूची-होस्टेड मूल्य-ट्यूपल्स से बाहर पूंछ बनाने के लिए संघर्ष करने में मदद करता है।