where T : System.ValueType
?
Using struct
as a generic constraint is functionally equivalent to a "ValueType" constraint. In .NET, a struct is a value type.
I think the example below covers a lot of use cases that one would expect from ValueType. The parameter type of T? allows nullables, and the type constraints restrict it to structs that implement IFormattable which is true for the common value types I can think of.
public void foo<T>(T? a) where T : struct, IFormattable
Note that this allows types such as decimal, datetime, timespan.
https://docs.microsoft.com/en-us/dotnet/api/system.iformattable?view=netcore-3.1
ValueType is not the base class of value types, it is simply a container for the value when it is boxed. Since it is a container class and not in any sort of hierarchy for the actual types you're wanting to use, it is not useful as a generic constraint.
There are two differences between using
where T : struct
and
where T : ValueType
T
to be ValueType
itself, which is a reference type.T
to be a nullable value typeThe first of these differences is almost never what you want. The second could occasionally be useful; Nullable<T>
is slightly odd in that it satisfies neither the where T : struct
nor where T : class
constraint.
More useful would be the constraint
where T : struct, System.Enum
which is prohibited by C# for no good reason that I can tell. See my blog post and the Unconstrained Melody project for more on this.