{"id":1225,"date":"2018-06-12T13:04:32","date_gmt":"2018-06-12T05:04:32","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/seteplia\/?p=1225"},"modified":"2019-06-11T21:22:33","modified_gmt":"2019-06-12T04:22:33","slug":"dissecting-new-generics-constraints-in-c-7-3","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/premier-developer\/dissecting-new-generics-constraints-in-c-7-3\/","title":{"rendered":"Dissecting new generic constraints in C# 7.3"},"content":{"rendered":"<p>During the last Build conference, Microsoft has announced the next version of Visual Studio with C# 7.3 support. This is yet another minor language update with some quite interesting features. The main change was related to generics, starting from C# 7.3 there 3 more constraints: <code>unmanaged<\/code>, <code>System.Enum<\/code> and <code>System.Delegate<\/code>.<\/p>\n<h4>The <code>unmanaged<\/code> constraint<\/h4>\n<p>The <code>unmanaged<\/code> constraint on generic type <code>T<\/code> enforces the type to be an &#8216;unmanaged&#8217; struct that does not recursively contain reference type fields. The concept of &#8216;unmanaged types&#8217; existed in the C# language for a long time and you can find this term in the C# language specification and in the official documentation, but now the compiler can actually enforce it.<\/p>\n<p>This feature could be useful for low-level scenarios, like native-managed interoperability, but could be helpful for common enterprise scenarios as well. An instance of &#8216;unmanaged&#8217; type <code>T<\/code> is convertible to <code>T*<\/code> which is convertible to <code>byte*<\/code> that opens some interesting possibilities. Let suppose you want to keep your data in a key-value store. To get a key you may consider using <code>SHA1<\/code> or some other hashing algorithm, but you don&#8217;t want to implement the hashing logic for every type in the system.<\/p>\n<p>Now you can implement this logic for all &#8216;unmanaged&#8217; types in your application the following way:<\/p>\n<pre class=\"lang:default decode:true\">public unsafe static byte[] ComputeHash&lt;T&gt;(T data) where T : unmanaged\r\n{\r\n    byte* bytes = (byte*)(&amp;data);\r\n    using (var sha1 = SHA1.Create())\r\n    {\r\n        var size = sizeof(T);\r\n        using (var ms = new UnmanagedMemoryStream(bytes, size))\r\n        {\r\n            return sha1.ComputeHash(ms);\r\n        }\r\n    }\r\n}<\/pre>\n<p>This code works perfectly fine because <code>sizeof<\/code> operator is actually designed for <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/keywords\/sizeof\">&#8216;unmanaged&#8217; types<\/a> even though there was no special constraint in the language to enforce the correctness of this operator at compile time.<\/p>\n<p>The feature is relatively simple but there are some caveats:<\/p>\n<ul>\n<li>Unmanaged types are different from <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/interop\/blittable-and-non-blittable-types\">&#8216;blittable types&#8217;<\/a>. Blittable types have the same representation in both managed and unmanaged code. This means that some &#8216;unmanaged&#8217; types are not blittable and some blittable types are not &#8216;unmanaged&#8217;. For instance, <code>bool<\/code> and <code>char<\/code> are &#8216;unmanaged&#8217; types but they&#8217;re not blittable, because the underlying representation for both of these types could be &#8220;platform-specific&#8221;. On the other hand, strings could be blittable in some cases, but they do not belong to a new family of &#8216;unmanaged&#8217; types because <code>string<\/code> is a reference type.<\/li>\n<li>Existing generic types are not &#8216;unmanaged&#8217;. <code>int?<\/code>, <code>(int, double)<\/code>, <code>System.KeyValuePair&lt;int, int&gt;<\/code> are not &#8216;unmanaged&#8217; types because these generic types don&#8217;t have the &#8216;unmanaged&#8217; constraint.<\/li>\n<li>You still <strong>can&#8217;t<\/strong> safely use <code>Marshal.SizeOf&lt;T&gt;<\/code> when <code>T<\/code> is an &#8216;unmanaged&#8217; type. Even though the documentation for <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/5s4920fa(v=vs.110).aspx\"><code>Marhal.SizeOf&lt;T&gt;<\/code><\/a> states that the method &#8220;Returns the size of an unmanaged type in bytes.&#8221;, it is not quite true today. Any enum is considered to be an unmanaged type but trying to use an enum type in <code>Marshal.SizeOf&lt;MyEnum&gt;()<\/code> will lead to a runtime error. For all other unmanaged types, this function works similar to <code>sizeof<\/code> operator.<\/li>\n<\/ul>\n<h4>The <code>Enum<\/code> constraint<\/h4>\n<p>The <code>System.Enum<\/code> constraint on type <code>T<\/code> enforces that the type is an enum. Enum types are not as ubiquitous as other primitive types, but this constraint still may be very useful in many scenarios.<\/p>\n<p>For instance, you can solve issues with the existing API provided by <code>System.Enum<\/code> type:<\/p>\n<pre class=\"lang:default decode:true \">public static TEnum[] GetValues&lt;TEnum&gt;() where TEnum : System.Enum\r\n{\r\n    return (TEnum[])Enum.GetValues(typeof(TEnum));\r\n}\r\n \r\n\r\n\/\/ BCL-based version\r\nMyEnum[] values = (MyEnum[])Enum.GetValues(typeof(MyEnum));\r\n            \r\n\/\/ Type-safe version\r\nMyEnum[] values2 = GetValues&lt;MyEnum&gt;();<\/pre>\n<p>But we can make a step forward and introduce a concept of <code>EnumTraits<\/code> &#8211; a special type that represents a metadata for a given enum type. For instance, we can get a min\/max value, available values, and some other information:<\/p>\n<pre class=\"lang:default decode:true \">public static class EnumTraits&lt;TEnum&gt; where TEnum : struct, Enum\r\n{\r\n    private static HashSet&lt;TEnum&gt; _valuesSet;\r\n    static EnumTraits()\r\n    {\r\n        var type = typeof(TEnum);\r\n        var underlyingType = Enum.GetUnderlyingType(type);\r\n \r\n        EnumValues = (TEnum[])Enum.GetValues(typeof(TEnum));\r\n        _valuesSet = new HashSet&lt;TEnum&gt;(EnumValues);\r\n \r\n        var longValues =\r\n            EnumValues\r\n            .Select(v =&gt; Convert.ChangeType(v, underlyingType))\r\n            .Select(v =&gt; Convert.ToInt64(v))\r\n            .ToList();\r\n \r\n        IsEmpty = longValues.Count == 0;\r\n        if (!IsEmpty)\r\n        {\r\n            var sorted = longValues.OrderBy(v =&gt; v).ToList();\r\n            MinValue = sorted.Min();\r\n            MaxValue = sorted.Max();\r\n        }\r\n    }\r\n \r\n    public static bool IsEmpty { get; }\r\n    public static long MinValue { get; }\r\n    public static long MaxValue { get; }\r\n    public static TEnum[] EnumValues { get; }\r\n \r\n    \/\/ This version is almost an order of magnitude faster then Enum.IsDefined\r\n    public static bool IsValid(TEnum value) =&gt; _valuesSet.Contains(value);\r\n}\r\n \r\nenum EnumLong : long\r\n{\r\n    X = -1,\r\n    Y = 1,\r\n    Z = 2,\r\n}\r\n \r\nConsole.WriteLine(EnumTraits&lt;EnumLong&gt;.MinValue); \/\/ -1\r\nConsole.WriteLine(EnumTraits&lt;EnumLong&gt;.MaxValue); \/\/ 2\r\nConsole.WriteLine(EnumTraits&lt;EnumLong&gt;.IsValid(0)); \/\/ False<\/pre>\n<p>We can go even further: in the static constructor we can look for a specific attribute that user may use to provide enum&#8217;s friendly name. We can check for duplicates and expose a property like <code>HasDuplicates<\/code>, we can check <code>FlagsAttribute<\/code> for an enum type and expose this information via <code>IsFlagsEnum<\/code> property. Or we can even implement a more efficient version of <code>Enum.HasFlag<\/code> that causes two boxing allocations per call.<\/p>\n<p>As you probably know <a href=\"https:\/\/blogs.msdn.microsoft.com\/dotnet\/2018\/04\/18\/performance-improvements-in-net-core-2-1\/\">.NET Core 2.1 has a lot of performance improvements<\/a>, including an optimized version of <code>Enum.HasFlag<\/code>. But unfortunately, not every application can benefit from these optimizations, so it makes sense to have a more efficient version of <code>Enum.HasFlag<\/code> for the other runtimes.<\/p>\n<p>But this is not as simple as you would think. The <code>Enum<\/code> constraint does not allow you to perform arithmetic operations on enum&#8217;s instances.<\/p>\n<pre class=\"lang:default decode:true\">public static long ToLong&lt;TEnum&gt;(TEnum value) where TEnum : System.Enum\r\n{\r\n    \/\/ Cannot convert 'TEnum' to 'long'\r\n    return (long)value;\r\n}<\/pre>\n<p>Every enum type implicitly implements <code>IConvertible<\/code> and you may try to leverage this:<\/p>\n<pre class=\"lang:default decode:true \">public static long ToLong&lt;TEnum&gt;(TEnum value) where TEnum : System.Enum, IConvertible\r\n{\r\n    return value.ToInt64(provider: null);\r\n}<\/pre>\n<p>This code works fine but causes boxing allocation for each invocation: the thing that we tried to avoid in the first place.<\/p>\n<p>The only remaining option that I could think of is the code generation. Even though different enum instances could be of a different size (<code>byte<\/code>, <code>short<\/code>, <code>int<\/code> &#8212; the default enum&#8217;s underlying type, or <code>long<\/code>) the IL-code that is required to convert an integral value of different types to a specific target type is the same. For instance, the IL-code for converting <code>byte<\/code>, <code>short<\/code> or <code>int<\/code> to a <code>long<\/code> is the same: <code>Conv_I8<\/code>:<\/p>\n<pre class=\"lang:default decode:true \">public static class EnumConverter\r\n{\r\n    public static Func&lt;T, long&gt; CreateConvertToLong&lt;T&gt;() where T : struct, Enum\r\n    {\r\n        var method = new DynamicMethod(\r\n            name: \"ConvertToLong\",\r\n            returnType: typeof(long),\r\n            parameterTypes: new[] { typeof(T) },\r\n            m: typeof(EnumConverter).Module,\r\n            skipVisibility: true);\r\n \r\n        ILGenerator ilGen = method.GetILGenerator();\r\n \r\n        ilGen.Emit(OpCodes.Ldarg_0);\r\n        ilGen.Emit(OpCodes.Conv_I8);\r\n        ilGen.Emit(OpCodes.Ret);\r\n        return (Func&lt;T, long&gt;)method.CreateDelegate(typeof(Func&lt;T, long&gt;));\r\n    }\r\n}<\/pre>\n<p>The implementation of an allocation-free version of <code>HasFlags<\/code> is fairly straightforward:<\/p>\n<pre class=\"lang:default decode:true\">public static class EnumExtensions\r\n{\r\n    \/\/\/ &lt;summary&gt;\r\n    \/\/\/ Allocation-free version of &lt;see cref=\"Enum.HasFlag(Enum)\"\/&gt;\r\n    \/\/\/ &lt;\/summary&gt;\r\n    public static bool HasFlags&lt;TEnum&gt;(this TEnum left, TEnum right) where TEnum: struct, Enum\r\n    {\r\n        var fn = Converter&lt;TEnum&gt;.ConverterFn;\r\n        return (fn(left) &amp; fn(right)) != 0;\r\n    }\r\n \r\n    private class Converter&lt;TEnum&gt; where TEnum: struct, Enum\r\n    {\r\n        public static readonly Func&lt;TEnum, long&gt; ConverterFn = EnumConverter.CreateConvertToLong&lt;TEnum&gt;();\r\n    }\r\n}<\/pre>\n<p>Let&#8217;s compare this implementation with <code>Enum.HasFlag<\/code> and with a simple bitwise comparison.<\/p>\n<pre class=\"lang:default decode:true\">                Method |       Mean |     Error |    StdDev | Scaled |    Gen 0 | Allocated | \r\n---------------------- |-----------:|----------:|----------:|-------:|---------:|----------:| \r\n           EnumHasFlag | 24.6219 ns | 0.4849 ns | 0.6798 ns |   1.00 | 152.3438 |  480005 B | \r\n        EnumExHasFlags |  7.4968 ns | 0.1374 ns | 0.1285 ns |   0.30 |        - |       0 B | \r\n EnumBitwiseComparison |  0.3752 ns | 0.0074 ns | 0.0134 ns |   0.02 |        - |       0 B |<\/pre>\n<p>As you can see, the optimized version is 3 times faster than the original <code>Enum.HasFlag<\/code>implementation and is allocation free. It is still way slower than a simple bit-wise comparison that consists only of a few assembly instructions.<\/p>\n<h5>The oddities of the <code>Enum<\/code> constraint<\/h5>\n<p>There is one very interesting caveat with the <code>Enum<\/code> constraint: the constraint does not imply but itself that the <code>T<\/code> is a struct. Moreover, you can actually combine the <code>Enum<\/code>constraint with the <code>class<\/code> constraint:<\/p>\n<pre class=\"lang:default decode:true \">\/\/ The only valid TEnum type is `System.Enum`\r\npublic static void Foo&lt;TEnum&gt;() where TEnum : class, Enum\r\n{ }\r\n        static void Main(string[] args)\r\n        {\r\n\/\/ Error 'EnumLong' must be a class\r\nFoo&lt;EnumLong&gt;();\r\n\/\/ Ok, but why would you do that?!?!?\r\nFoo&lt;System.Enum&gt;();<\/pre>\n<p>The combination <code>where TEnum: class, Enum<\/code> makes no sense and the only reasonable way to use the <code>Enum<\/code> constraint is to use it with the <code>struct<\/code> constraint: <code>where TEnum: struct, Enum<\/code>.<\/p>\n<h4>The <code>Delegate<\/code> constraint<\/h4>\n<p>The <code>System.Delegate<\/code> constraint on type <code>T<\/code> enforces that the type is a delegate. I think this is the least useful constraint from all the new ones, but I can definitely see the value of it in some scenarios.<\/p>\n<p>In the previous section, we&#8217;ve used code generation to work-around some restrictions for the <code>Enum<\/code> constraint. The solution was relatively simple but it may be improved by creating a facade class with the new <code>Delegate<\/code> constraint.<\/p>\n<pre class=\"lang:default decode:true \">\/\/\/ Non-generic facade for creating dynamic methods for a given delegate type\r\n\/\/\/ &lt;\/summary&gt;\r\npublic static class DynamicMethodGenerator\r\n{\r\n    public static DynamicMethodGenerator&lt;TDelegate&gt; Create&lt;TDelegate&gt;(string name)\r\n        where TDelegate : Delegate\r\n        =&gt; new DynamicMethodGenerator&lt;TDelegate&gt;(name);\r\n}\r\n \r\n\/\/\/ &lt;summary&gt;\r\n\/\/\/ Generator class for constructing delegates of type &lt;typeparamref name=\"TDelegate\"\/&gt;.\r\n\/\/\/ &lt;\/summary&gt;\r\npublic sealed class DynamicMethodGenerator&lt;TDelegate&gt; where TDelegate : Delegate\r\n{\r\n    private readonly DynamicMethod _method;\r\n \r\n    internal DynamicMethodGenerator(string name)\r\n    {\r\n        MethodInfo invoke = typeof(TDelegate).GetMethod(\"Invoke\");\r\n \r\n        var parameterTypes = invoke.GetParameters().Select(p =&gt; p.ParameterType).ToArray();\r\n \r\n        _method = new DynamicMethod(name, invoke.ReturnType, \r\n            parameterTypes, restrictedSkipVisibility: true);\r\n    }\r\n \r\n    public ILGenerator GetILGenerator() =&gt; _method.GetILGenerator();\r\n \r\n    public TDelegate Generate()\r\n    {\r\n        return (TDelegate)_method.CreateDelegate(typeof(TDelegate));\r\n    }\r\n}<\/pre>\n<p>This simple class drastically simplifies the code generation and makes it less error-prone:<\/p>\n<pre class=\"lang:default decode:true \">public static Func&lt;T, long&gt; CreateConvertToLong&lt;T&gt;() where T : struct, Enum\r\n{\r\n    var generator = DynamicMethodGenerator.Create&lt;Func&lt;T, long&gt;&gt;(\"ConvertToLong\");\r\n \r\n    ILGenerator ilGen = generator.GetILGenerator();\r\n \r\n    ilGen.Emit(OpCodes.Ldarg_0);\r\n    ilGen.Emit(OpCodes.Conv_I8);\r\n    ilGen.Emit(OpCodes.Ret);\r\n    return generator.Generate();\r\n}<\/pre>\n<h4>Conclusion<\/h4>\n<p>None of the new constraints are game changing features but they definitely can be quite useful in real life scenarios.<\/p>\n<ul>\n<li>The <code>unmanaged<\/code> constraint is useful for native-managed interop scenarios as well as for more widely use cases like serialization\/deserialization or hash computations.<\/li>\n<li>The <code>Enum<\/code> constraint helps to work-around existing issues with utility methods from <code>System.Enum<\/code> type, or for creating application-specific &#8220;enum traits&#8221; types for custom validation, enum-ToString conversion etc.<\/li>\n<li>The <code>Delegate<\/code> constraint is helpful for code generation but may be helpful in other cases as well. For instance, when dealing with expression trees, for creating generic event handlers or for implementing command pattern.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>During the last Build conference, Microsoft has announced the next version of Visual Studio with C# 7.3 support. This is yet another minor language update with some quite interesting features. The main change was related to generics, starting from C# 7.3 there 3 more constraints: unmanaged, System.Enum and System.Delegate. The unmanaged constraint The unmanaged constraint [&hellip;]<\/p>\n","protected":false},"author":4004,"featured_media":37840,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[6699],"tags":[6695],"class_list":["post-1225","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c","tag-seteplia"],"acf":[],"blog_post_summary":"<p>During the last Build conference, Microsoft has announced the next version of Visual Studio with C# 7.3 support. This is yet another minor language update with some quite interesting features. The main change was related to generics, starting from C# 7.3 there 3 more constraints: unmanaged, System.Enum and System.Delegate. The unmanaged constraint The unmanaged constraint [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/1225","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/users\/4004"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/comments?post=1225"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/1225\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media\/37840"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media?parent=1225"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/categories?post=1225"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/tags?post=1225"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}