Parenthesis Tuple(?) Syntax in C# - c#

I just saw the following syntax in code: (int, string) testTwo
It seems to mimic a Tuple, but the return types are incompatible with a Tuple. Example:
Tuple<int, string> test = Test.Get(); // This doesn't work
(int, string) testTwo = Test.Get(); // This works
public static class Test
{
public static (int, string) Get()
{
return (1, "2");
}
}
Seems like you can name the params too, but this appears to be for readability only, e.g.:
public static (int foo, string bar) Get()
What is this syntax called?
What's the real world use case?

There are two tuple types in the modern C#/.NET - "old" ones - series of Tuple classes and value tuples introduced in C# 7 which are syntactic sugar based on series of ValueTuple structs and there is no conversions between those two (though both can be deconstructed - var (i1, i2) = Tuple.Create(1,2); and var (i1, i2) = (1,2); both are a valid code).
Tuples vs System.Tuple
C# tuples, which are backed by System.ValueTuple types, are different from tuples that are represented by System.Tuple types. The main differences are as follows:
System.ValueTuple types are value types. System.Tuple types are reference types.
System.ValueTuple types are mutable. System.Tuple types are immutable.
Data members of System.ValueTuple types are fields. Data members of System.Tuple types are properties.

When creating a Tuple in parentheses it's a value type, specifically it's a System.ValueTuple. System.Tuple is a reference type.

It's a Tuple type which maps to System.ValueTuple, a value type, available in .NET Core and Framework 4.7+.
A ValueTuple and a Tuple are distinct, incompatible types.
The syntax with parentheses is tuple assignment and deconstruction.

Related

I can convert List<string> to IEnumerable<object>, but I can't convert List<int> to IEnumerable<object>. Why not? [duplicate]

IEnumerable<T> is co-variant but it does not support value type, just only reference type. The below simple code is compiled successfully:
IEnumerable<string> strList = new List<string>();
IEnumerable<object> objList = strList;
But changing from string to int will get compiled error:
IEnumerable<int> intList = new List<int>();
IEnumerable<object> objList = intList;
The reason is explained in MSDN:
Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.
I have searched and found that some questions mentioned the reason is boxing between value type and reference type. But it does not still clear up my mind much why boxing is the reason?
Could someone please give a simple and detailed explanation why covariance and contravariance do not support value type and how boxing affects this?
Basically, variance applies when the CLR can ensure that it doesn't need to make any representational change to the values. References all look the same - so you can use an IEnumerable<string> as an IEnumerable<object> without any change in representation; the native code itself doesn't need to know what you're doing with the values at all, so long as the infrastructure has guaranteed that it will definitely be valid.
For value types, that doesn't work - to treat an IEnumerable<int> as an IEnumerable<object>, the code using the sequence would have to know whether to perform a boxing conversion or not.
You might want to read Eric Lippert's blog post on representation and identity for more on this topic in general.
EDIT: Having reread Eric's blog post myself, it's at least as much about identity as representation, although the two are linked. In particular:
This is why covariant and contravariant conversions of interface and delegate types require that all varying type arguments be of reference types. To ensure that a variant reference conversion is always identity-preserving, all of the conversions involving type arguments must also be identity-preserving. The easiest way to ensure that all the non-trivial conversions on type arguments are identity-preserving is to restrict them to be reference conversions.
It is perhaps easier to understand if you think about the underlying representation (even though this really is an implementation detail). Here is a collection of strings:
IEnumerable<string> strings = new[] { "A", "B", "C" };
You can think of the strings as having the following representation:
[0] : string reference -> "A"
[1] : string reference -> "B"
[2] : string reference -> "C"
It is a collection of three elements, each being a reference to a string. You can cast this to a collection of objects:
IEnumerable<object> objects = (IEnumerable<object>) strings;
Basically it is the same representation except now the references are object references:
[0] : object reference -> "A"
[1] : object reference -> "B"
[2] : object reference -> "C"
The representation is the same. The references are just treated differently; you can no longer access the string.Length property but you can still call object.GetHashCode(). Compare this to a collection of ints:
IEnumerable<int> ints = new[] { 1, 2, 3 };
[0] : int = 1
[1] : int = 2
[2] : int = 3
To convert this to an IEnumerable<object> the data has to be converted by boxing the ints:
[0] : object reference -> 1
[1] : object reference -> 2
[2] : object reference -> 3
This conversion requires more than a cast.
I think everything starts from definiton of LSP (Liskov Substitution Principle), which climes:
if q(x) is a property provable about objects x of type T then q(y) should be true for objects y of type S where S is a subtype of T.
But value types, for example int can not be substitute of object in C#.
Prove is very simple:
int myInt = new int();
object obj1 = myInt ;
object obj2 = myInt ;
return ReferenceEquals(obj1, obj2);
This returns false even if we assign the same "reference" to the object.
It does come down to an implementation detail: Value types are implemented differently to reference types.
If you force value types to be treated as reference types (i.e. box them, e.g. by referring to them via an interface) you can get variance.
The easiest way to see the difference is simply consider an Array: an array of Value types are put together in memory contiguously (directly), where as an array of Reference types only have the reference (a pointer) contiguously in memory; the objects being pointed to are separately allocated.
The other (related) issue(*) is that (almost) all Reference types have the same representation for variance purposes and much code does not need to know of the difference between types, so co- and contra-variance is possible (and easily implemented -- often just by omission of extra type checking).
(*) It may be seen to be the same issue...

Array with mixed data-types in C#

Given the following code:
void Main()
{
dynamic[] arr = { 5, "test2", "test3"};
foreach (var i in arr)
{
Console.WriteLine(i.GetType().Name);
}
}
it prints the following:
Int32
String
String
I can't understand how can an array has elements of different types. From a C background, array elements should be having the same type and each element should take the same amount of RAM. Because in C, something like arr[i] would be equivalent to *(arr + i) and the pointer arr would move i * sizeof(arr data type) steps.
dynamic[] arr = { 5, "test2", "test3"};
results in object[] (you can see if you call arr.GetType()).
The array contains objects of the same type; in this case the type is object.
Boxing and unboxing
The elements in your array are boxed. This passage is from Boxing and Unboxing (C# Programming Guide).
Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type. When the CLR boxes a value type, it wraps the value inside a System.Object instance and stores it on the managed heap.
An object[] array, even for value types, does not contain the objects themselves; it contains references to them (btw. string is a reference type in C#).
Again, this is from Boxing and Unboxing (C# Programming Guide).
dynamic in C#
I think the first sentence from Using type dynamic (C# Programming Guide) could clarify how dynamic works in C#.
C# 4 introduces a new type, dynamic. The type is a static type, but an object of type dynamic bypasses static type checking.
The quote from Built-in reference types (C# reference) may even be better.
The dynamic type indicates that use of the variable and references to its members bypass compile-time type checking. Instead, these operations are resolved at run time. (...)
Type dynamic behaves like type object in most circumstances.
Remember that in C#, all classes inherit from Object.
An Object[] array is actually an array containing pointers to the actual objects, so the size is always the same.
The memory would look like this :
A dynamic[] array will be casted to Object[], therefore accepting any data type in there.
About structure, which don't inherit from Object, the run-time uses a trick called boxing to put the structure inside an object, therefore allowing the structure item to enter the array.
A dynamic type will be stored as an object but at run time the compiler will load many more bytes to make sense of what to do with the dynamic type. In order to do that, a lot more memory will be used to figure that out. Think of dynamic as a fancy object.

Why I can't use value types in generic class with constraint where T : IEnumerable<object> [duplicate]

IEnumerable<T> is co-variant but it does not support value type, just only reference type. The below simple code is compiled successfully:
IEnumerable<string> strList = new List<string>();
IEnumerable<object> objList = strList;
But changing from string to int will get compiled error:
IEnumerable<int> intList = new List<int>();
IEnumerable<object> objList = intList;
The reason is explained in MSDN:
Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.
I have searched and found that some questions mentioned the reason is boxing between value type and reference type. But it does not still clear up my mind much why boxing is the reason?
Could someone please give a simple and detailed explanation why covariance and contravariance do not support value type and how boxing affects this?
Basically, variance applies when the CLR can ensure that it doesn't need to make any representational change to the values. References all look the same - so you can use an IEnumerable<string> as an IEnumerable<object> without any change in representation; the native code itself doesn't need to know what you're doing with the values at all, so long as the infrastructure has guaranteed that it will definitely be valid.
For value types, that doesn't work - to treat an IEnumerable<int> as an IEnumerable<object>, the code using the sequence would have to know whether to perform a boxing conversion or not.
You might want to read Eric Lippert's blog post on representation and identity for more on this topic in general.
EDIT: Having reread Eric's blog post myself, it's at least as much about identity as representation, although the two are linked. In particular:
This is why covariant and contravariant conversions of interface and delegate types require that all varying type arguments be of reference types. To ensure that a variant reference conversion is always identity-preserving, all of the conversions involving type arguments must also be identity-preserving. The easiest way to ensure that all the non-trivial conversions on type arguments are identity-preserving is to restrict them to be reference conversions.
It is perhaps easier to understand if you think about the underlying representation (even though this really is an implementation detail). Here is a collection of strings:
IEnumerable<string> strings = new[] { "A", "B", "C" };
You can think of the strings as having the following representation:
[0] : string reference -> "A"
[1] : string reference -> "B"
[2] : string reference -> "C"
It is a collection of three elements, each being a reference to a string. You can cast this to a collection of objects:
IEnumerable<object> objects = (IEnumerable<object>) strings;
Basically it is the same representation except now the references are object references:
[0] : object reference -> "A"
[1] : object reference -> "B"
[2] : object reference -> "C"
The representation is the same. The references are just treated differently; you can no longer access the string.Length property but you can still call object.GetHashCode(). Compare this to a collection of ints:
IEnumerable<int> ints = new[] { 1, 2, 3 };
[0] : int = 1
[1] : int = 2
[2] : int = 3
To convert this to an IEnumerable<object> the data has to be converted by boxing the ints:
[0] : object reference -> 1
[1] : object reference -> 2
[2] : object reference -> 3
This conversion requires more than a cast.
I think everything starts from definiton of LSP (Liskov Substitution Principle), which climes:
if q(x) is a property provable about objects x of type T then q(y) should be true for objects y of type S where S is a subtype of T.
But value types, for example int can not be substitute of object in C#.
Prove is very simple:
int myInt = new int();
object obj1 = myInt ;
object obj2 = myInt ;
return ReferenceEquals(obj1, obj2);
This returns false even if we assign the same "reference" to the object.
It does come down to an implementation detail: Value types are implemented differently to reference types.
If you force value types to be treated as reference types (i.e. box them, e.g. by referring to them via an interface) you can get variance.
The easiest way to see the difference is simply consider an Array: an array of Value types are put together in memory contiguously (directly), where as an array of Reference types only have the reference (a pointer) contiguously in memory; the objects being pointed to are separately allocated.
The other (related) issue(*) is that (almost) all Reference types have the same representation for variance purposes and much code does not need to know of the difference between types, so co- and contra-variance is possible (and easily implemented -- often just by omission of extra type checking).
(*) It may be seen to be the same issue...

Why does ToList<Interface> not work for value types?

If I implement an interface for a value type and try to cast it to a List of it's interface type, why does this result in an error whereas the reference type converts just fine?
This is the error:
Cannot convert instance argument type
System.Collections.Generic.List<MyValueType> to
System.Collections.Generic.IEnumerable<MyInterfaceType>
I have to explicitely use the Cast<T> method to convert it, why?
Since IEnumerable is a readonly enumeration through a collection, it doesn't make any sense to me that it cannot be cast directly.
Here's example code to demonstrate the issue:
public interface I{}
public class T : I{}
public struct V: I{}
public void test()
{
var listT = new List<T>();
var listV = new List<V>();
var listIT = listT.ToList<I>(); //OK
var listIV = listV.ToList<I>(); //FAILS to compile, why?
var listIV2 = listV.Cast<I>().ToList(); //OK
}
Variance (covariance or contravariance) doesn't work for value types, only reference types:
Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type. (MSDN)
The values contained inside reference type variables are references (for example, addresses) and data addresses have the same size and are interpreted the same way, without any required change in their bit patterns.
In contrast, the values contained inside value type variables do not have the same size or the same semantics. Using them as reference types requires boxing and boxing requires type-specific instructions to be emitted by the compiler. It's not practical or efficient (sometimes maybe not even possible) for the compiler to emit boxing instructions for any possible kind of value type, therefore variance is disallowed altogether.
Basically, variance is practical thanks to the extra layer of indirection (the reference) from the variable to the actual data. Because value types lack that layer of indirection, they lack variance capabilities.
Combine the above with how LINQ operations work:
A Cast operation upcasts/boxes all elements (by accessing them through the non-generic IEnumerable, as you pointed out) and then verifies that all elements in a sequence can be successfully cast/unboxed to the provided type and then does exactly that. The ToList operation enumerates the sequence and returns a list from that enumeration.
Each one has its own job. If (say) ToList did the job of both, it would have the performance overhead of both, which is undesirable for most other cases.

List, array and IEnumerable covariance

I'll start with several postulates to better explain the context of my question:
Array Covariance
Postulate 1.1
An array of a value type is not covariant. int[] cannot pass for object[].
Postulate 1.2
An array of a reference type is covariant with a valid IEnumerable. string[] can pass for IEnumerable<object>).
Postulate 1.3
An array of a reference type is covariant with a valid covariant array. string[] can pass for object[].
List Covariance
Postulate 2.1 (same as 1.1)
A list of a value type is not covariant. List<int> cannot pass for List<object>.
Postulate 2.2 (same as 1.2)
A list of a reference type is covariant with a valid IEnumerable. List<string> can pass for IEnumerable<object>).
Postulate 2.3 (different from 1.3)
A list of a reference type is not covariant with a valid covariant List. List<string> cannot pass for List<object>).
My question concerns postulates 1.3, 2.2 and 2.3. Specifically:
Why can string[] pass for object[], but List<string> not for List<object>?
Why can List<string> pass for IEnumerable<object> but not for List<object>?
List covariance is unsafe:
List<string> strings = new List<string> { "a", "b", "c" };
List<object> objects = strings;
objects.Add(1); //
Array covariance is also unsafe for the same reason:
string[] strings = new[] { "a", "b", "c" };
object[] objects = strings;
objects[0] = 1; //throws ArrayTypeMismatchException
array covariance in C# is recognised as a mistake, and has been present since version 1.
Since the collection cannot be modified through the IEnumerable<T> interface, it is safe to type a List<string> as an IEnumerable<object>.
Arrays are covariant, but a System.Int32[] does not hold references to things which are derived from System.Object. Within the .NET runtime, each value-type definition actually defines two kinds of things: a heap object type and a value (storage location) type. The heap object type is derived from System.Object; the storage location type is implicitly convertible to the heap object type (which in turn derives from System.Object) but does not itself actually derive from System.Object nor anything else. Although all arrays, including System.Int32[] are heap-object types, the individual elements of a System.Int32[] are instances of the storage location type.
The reason that a String[] can be passed to code expecting an Object[] is that the former contains "references to heap-object instances of type derived from type String", and the latter likewise for type Object. Since String derives from Object, a reference to a heap-object of a type derived from String will also be a reference to a heap object which derives from Object, and a String[] will contain references to heap objects which derive from Object--exactly what code would expect to read from an Object[]. By contrast, because an int[] [i.e. System.Int32[]] does not contain references to heap-object instances of type Int32, its contents will not conform to the expectations of code which is expecting Object[].

Categories

Resources