System.ValueTuple과 System.Tuple의 차이점은 무엇입니까?
일부 C # 7 라이브러리를 디 컴파일하고 ValueTuple
제네릭이 사용되는 것을 보았습니다 . 대신 무엇 ValueTuples
이며 왜 그렇지 Tuple
않습니까?
- https://docs.microsoft.com/en-gb/dotnet/api/system.tuple
- https://docs.microsoft.com/en-gb/dotnet/api/system.valuetuple
대신 무엇
ValueTuples
이며 왜 그렇지Tuple
않습니까?
A ValueTuple
는 원래 System.Tuple
클래스 와 같은 튜플을 반영하는 구조체입니다 .
와의 주요 차이점은 다음 Tuple
과 ValueTuple
같습니다.
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.
'development' 카테고리의 다른 글
SQL Server 버전을 확인하는 방법 (0) | 2020.07.22 |
---|---|
재정의 된 설명 방법으로 개체의 메모리 주소를 NSLog (0) | 2020.07.22 |
C #에서 UUID를 생성하는 방법 (0) | 2020.07.22 |
Excel에서 밀리 초 표시 (0) | 2020.07.22 |
키보드 입력을 읽는 방법? (0) | 2020.07.22 |