Consider the following types:
(int, int)
→ managed.struct MyStruct { public (int,int) Value; }
→ unmanaged!
Problem: A non-generic structure MyStruct
, which has a managed member (int,int)
has been evaluated as managed type.
Expected Behavior: A structure which contains a managed member, should be considered as managed, the same way the struct MyStruct { int? Value; }
are considered as managed.
It seems both types are behaving against the documentations [1] and [2].
Example 1 - unmanaged Constraint
class Program
{
static void DoSomething<T>() where T : unmanaged { }
struct MyStruct { public (int, int) Value; }
static void Main(string[] args)
{
DoSomething<MyStruct>(); // → OK
DoSomething<(int, int)>(); // → Shows compile-time error
}
}
Error CS8377 The type '(int, int)' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Program.DoSomething()'
Example 2 - pointer or sizeof
Using above structure, the behavior is the same for pointers or sizeof
operator:
unsafe
{
(int, int)* p1; // → Compile-time error,
MyStruct* p2; // → Compiles
}
Error CS0208 Cannot take the address of, get the size of, or declare a pointer to a managed type('(int, int)')
Question
How do a struct containing
ValueTuple
is considered asunmanaged
and can satisfyunmanaged
constraint while theValueTuple
is considered as managed?How a struct having
ValueTupple<T1, T2>
and a struct containingNullable<T>
are treated differently?
Note 1: IMO the issue is different from the Proposal: Unmanaged constructed types (addressed by DavidG in comments), because MyStruct
is not generic, on the other hand while int?
and (int,int)
both are managed, but struct MyStruct { int? Value; }
and struct MyStruct { (int, int) Value; }
evaluated differently.
Thanks for reporting. This is just a bug in the compiler. The tuple when used as a field should be registering as a generic type and hence invalid in an
unmanaged type
. It appears to be evaluating as a tulpe instead and missing this check.Good news is that in C# 8.0 this restriction will be going away. The type
(int, int)
is a validunmanaged type
.