development

System.ValueTuple과 System.Tuple의 차이점은 무엇입니까?

big-blog 2020. 7. 22. 07:35
반응형

System.ValueTuple과 System.Tuple의 차이점은 무엇입니까?


일부 C # 7 라이브러리를 디 컴파일하고 ValueTuple제네릭이 사용되는 것을 보았습니다 . 대신 무엇 ValueTuples이며 왜 그렇지 Tuple않습니까?


대신 무엇 ValueTuples이며 왜 그렇지 Tuple않습니까?

A ValueTuple는 원래 System.Tuple클래스 와 같은 튜플을 반영하는 구조체입니다 .

와의 주요 차이점은 다음 TupleValueTuple같습니다.

  • System.ValueTuple값 유형 (struct)이고 System.Tuple참조 유형 ( class)입니다. 이는 할당 및 GC 압력에 대해 이야기 할 때 의미가 있습니다.
  • System.ValueTuple뿐만 아니라이 struct, 그것의 가변 , 다른 하나는로를 사용할 때주의해야한다. 수업 System.ValueTuple에서 필드를 가질 때 어떤 일이 발생하는지 생각해보십시오 .
  • System.ValueTuple 속성 대신 필드를 통해 항목을 노출합니다.

C # 7까지는 튜플을 사용하는 것이 그리 편리하지 않았습니다. 필드 이름은 Item1, Item2등이며 언어는 다른 대부분의 언어 (Python, Scala)와 마찬가지로 구문 설탕을 제공하지 않았습니다.

.NET 언어 디자인 팀이 언어 수준에서 튜플을 통합하고 구문 설탕을 추가하기로 결정했을 때 중요한 요소는 성능이었습니다. 함께 ValueTuple있기 때문에 (구현 세부로)를 사용할 때 값 형식 인, 당신은 그들이 스택에 할당됩니다 GC 압력을 피할 수 있습니다.

또한 struct런타임에 의해 자동 (얕은) 평등 의미론을 얻습니다 class. 디자인 팀은 튜플에 대해 더욱 최적화 된 평등이 보장되도록했지만이를 위해 커스텀 평등을 구현했습니다.

다음은 디자인 노트의Tuples 단락 입니다 .

구조 또는 클래스 :

언급했듯이, 할당 형벌과 관련이 없도록 튜플 유형을 만드는 structs것이 좋습니다 classes. 가능한 한 가벼워 야합니다.

틀림없이, structs때문에 할당 사본 더 큰 가치, 비용이 많이 드는 끝나게 수 있습니다. 따라서 생성 된 것보다 더 많은 것이 할당되면 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}");

컴파일러 장점 :

If we look under the cover of our previous example, we can see exactly how the compiler is interpreting ValueTuple when we ask it to deconstruct:

[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;
}

Internally, the compiled code utilizes Item1 and Item2, but all of this is abstracted away from us since we work with a decomposed tuple. A tuple with named arguments gets annotated with the TupleElementNamesAttribute. If we use a single fresh variable instead of decomposing, we get:

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));
}

Note that the compiler still has to make some magic happen (via the attribute) when we debug our application, as it would be odd to see Item1, Item2.


The difference between Tuple and ValueTuple is that Tuple is a reference type and ValueTuple is a value type. The latter is desirable because changes to the language in C# 7 have tuples being used much more frequently, but allocating a new object on the heap for every tuple is a performance concern, particularly when it's unnecessary.

However, in C# 7, the idea is that you never have to explicitly use either type because of the syntax sugar being added for tuple use. For example, in C# 6, if you wanted to use a tuple to return a value, you would have to do the following:

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

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

However, in C# 7, you can use this:

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

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

You can even go a step further and give the values names:

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

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

... Or deconstruct the tuple entirely:

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

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

Tuples weren't often used in C# pre-7 because they were cumbersome and verbose, and only really used in cases where building a data class/struct for just a single instance of work would be more trouble than it was worth. But in C# 7, tuples have language-level support now, so using them is much cleaner and more useful.


I looked at the source for both Tuple and ValueTuple. The difference is that Tuple is a class and ValueTuple is a struct that implements IEquatable.

That means that Tuple == Tuple will return false if they are not the same instance, but ValueTuple == ValueTuple will return true if they are of the same type and Equals returns true for each of the values they contain.


Other answers forgot to mention important points.Instead of rephrasing, I'm gonna reference the XML documentation from source code:

The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies tuples in C# and struct tuples in F#.

Aside from created via language syntax, they are most easily created via the ValueTuple.Create factory methods. The System.ValueTuple types differ from the System.Tuple types in that:

  • they are structs rather than classes,
  • they are mutable rather than readonly, and
  • their members (such as Item1, Item2, etc) are fields rather than properties.

With introduction of this type and C# 7.0 compiler, you can easily write

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

And return two values from a method:

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

Contrary to System.Tuple you can update its members (Mutable) because they are public read-write Fields that can be given meaningful names:

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

In addition to the comments above, one unfortunate gotcha of ValueTuple is that, as a value type, the named arguments get erased when compiled to IL, so they're not available for serialisation at runtime.

i.e. Your sweet named arguments will still end up as "Item1", "Item2", etc. when serialised via e.g. Json.NET.


Late-joining to add a quick clarification on these two factoids:

  • they are structs rather than classes
  • they are mutable rather than readonly

One would think that changing value-tuples en-masse would be straightforward:

 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;

Someone might try to workaround this like so:

 // 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'

The reason for this quirky behavior is that the value-tuples are exactly value-based (structs) and thus the .Select(...) call works on cloned-structs rather than on the originals. To resolve this we must resort to:

 // 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

Alternatively of course one might try the straightforward approach:

   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

Hope this helps someone struggling to make heads of tails out of list-hosted value-tuples.

참고URL : https://stackoverflow.com/questions/41084411/whats-the-difference-between-system-valuetuple-and-system-tuple

반응형