<p>In C++, templated code are compiled multiple times according to the parameters. But I just realized how restrictive C# generics are:</p>
<ul>
<li>The interfaces you are using on variables in a generic parameter type must be specified on the parameter.</li>
<li>There isn't a feature like explicit template specification, to get unrelated types from different types as inputs.</li>
<li>The implementable interface members are technically not called virtual, but just like virtual. They need lookups anyway, and there is nothing to optimize.</li>
</ul>
<p>There is a case that seems to require compiling multiple times, that is to define variables of different sizes. But most types in C# are references of fixed size. For the purpose of this question, let's assume variables are always references of fixed size and ignore other cases in a hypothetical language.</p>
<p>In this case, are there benefits of compiling this kind of generics to multiple instances of target code? If there are, where and why? If there are not, is this the exhaustive list of reasons why it doesn't need recompiling like C++? It's also helpful to know how C# is really implemented.</p>
<p>(They are termed monomorphization and type-erasure as two complication strategies. My question is, in this specific case, do they actually make any differences, to make it even meaningful to choose between something in a strategy?)</p>

<generics><c#><monomorphization><type-erasure>
Does the C# type of generics need compiling to multiple instances of target code?
CC BY-SA 4.0
2024-08-02T12:07:00.877Z
I think C# generics are code-generated once for reference types, and once for every value type (or perhaps once for every value-type size, as you say). Hope someone knowledgable (hint hint) will confirm/refute/explain. :-)
Pablo H
CC BY-SA 4.0
2024-08-03T23:03:30.077Z
The term "recompilation" has a particular meaning, that source code needs to be recompiled, for example, because a dependency on another source file where something significant (like signature) has changed.  I don't think that's the meaning you're going for however.
Erik Eidt
CC BY-SA 4.0
2024-08-03T23:07:49.990Z
The .NET JIT will make multiple copies of generated code for generics for the various type parameters when they are scalar/built-in types, because that improves code performance quite a bit over boxing.  Otherwise all reference types share the same generated code, as far as I know from a few years ago.
Erik Eidt
<p>When <code>T</code> is a ValueType (i.e a primitive or a <code>struct</code>), the generic will be monomorphized and there will be one implementation for that specific type (even if two types have the same behaviour for that method).</p>
<p>When <code>T</code> is <em>not</em> a ValueType, (an <code>interface</code> or a <code>class</code>) there is one shared method that uses the object's vtable to dispatch the appropriate calls for the type.</p>
<p>There is a common pattern in high-performance C# of constraining to a struct and interface that uses static types in order to force monomorphization:</p>
<pre class="lang-cs prettyprint-override"><code>interface IFoo
{
    int Transform(int a);
}

struct Bar : IFoo
{
    public int Transform(int a) =&gt; a * 2;
}

// ...

// the usage of forcing T to be a struct is this code will be monomorphized
// and `IFoo.Transform` can undergo inlining, whereas for a traditional 
// interface it will be a slow multi-dispatch call
void MapFoo&lt;T&gt;(int[] arr) where T : struct, IFoo
{
    for (var i = 0; i &lt; arr.Length; i++)
    {
        arr[i] = default(T).Transform(arr[i]);
    }
}
</code></pre>

CC BY-SA 4.0
2024-08-07T08:46:32.010Z
2024-08-07T09:25:51.623Z
2024-08-07T09:25:51.623Z
John
<p>I'm not sure what the official .NET implementation does these days, but from what I remember from working on an ahead-of-time compiler for C# a while back, the main constraints that limit sharing code between generic instantiations are:</p>
<p>(all examples assume that <code>T</code> is a generic parameter)</p>
<h2>Size</h2>
<p>Two instantiations where <code>T</code> has different size will require different code for loading, storing, and copying variables of type T. Similarly, if T is used as the type of a field, other fields of the class may have different field offsets. So, instantiations for <code>int</code> and <code>long</code> will probably not share the same code. You could still share code for any method that doesn't actually need to know the size of <code>T</code>. For example, on <code>List&lt;int&gt;.get_Size()</code> and <code>List&lt;long&gt;.get_Size()</code> might well be able to share code, even though <code>List&lt;int&gt;.Add()</code> and <code>List&lt;long&gt;.Add()</code> probably wouldn't share code.</p>
<h2>GC Layout</h2>
<p>If <code>T</code> is a <code>struct</code>, then, in addition to knowing the <em>size</em> of <code>T</code>, the code generator may need to know which offsets within an instance of <code>T</code> hold GC references, so that it can report all GC references to the garbage collector. So, the instantiation where <code>T</code> is a <code>struct</code> containing two <code>long</code> fields will probably not share code with the instantiation where <code>T</code> is a <code>struct</code> containing one <code>long</code> field and one <code>string</code> field, even though both <code>struct</code>s are 16 bytes in size.</p>
<h2>Uses of <code>T</code> without an object</h2>
<p>There are several situations in which the type of <code>T</code> must be known at runtime. The most common are casts to type <code>T</code> (including <code>is</code> and <code>as</code>), instantiating an instance of <code>T</code> (where <code>T</code> has the <code>new()</code> constraint), and <code>typeof(T)</code>.</p>
<p>Unlike the &quot;size&quot; and &quot;GC layout&quot; cases above, there's a reasonably efficient way to share the code between instantiations even if type of <code>T</code> is required at runtime. If <code>T</code> is a generic parameter on a generic class <code>C&lt;T&gt;</code>, the code generator can store the runtime type of <code>T</code> in the vtable of each instantiation of <code>C&lt;T&gt;</code>. This allows the code for all instance methods of <code>C&lt;T&gt;</code> to have access to the runtime type without bloating the size of every instance of <code>C&lt;T&gt;</code>. For static methods, and for the case where <code>T</code> is a generic parameter on a generic <em>method</em>, the code generator can generate a hidden extra parameter for that method, in which the caller is expected to pass a pointer to the runtime type information for that instantiation's generic parameters.</p>

CC BY-SA 4.0
2024-08-06T21:37:54.457Z
2024-08-06T21:37:54.457Z
Dave Bartolomeo
# Does the C# type of generics need compiling to multiple instances of target code?
Posted by: username_0 on 2024-08-01
In C++, templated code are compiled multiple times according to the parameters. But I just realized how restrictive C# generics are:

* The interfaces you are using on variables in a generic parameter type must be specified on the parameter.
* There isn't a feature like explicit template specification, to get unrelated types from different types as inputs.
* The implementable interface members are technically not called virtual, but just like virtual. They need lookups anyway, and there is nothing to optimize.

There is a case that seems to require compiling multiple times, that is to define variables of different sizes. But most types in C# are references of fixed size. For the purpose of this question, let's assume variables are always references of fixed size and ignore other cases in a hypothetical language.

In this case, are there benefits of compiling this kind of generics to multiple instances of target code? If there are, where and why? If there are not, is this the exhaustive list of reasons why it doesn't need recompiling like C++? It's also helpful to know how C# is really implemented.

(They are termed monomorphization and type-erasure as two complication strategies. My question is, in this specific case, do they actually make any differences, to make it even meaningful to choose between something in a strategy?)
**Tags:** generics c# monomorphization type-erasure
> **Comments:**
> **username_1**: I think C# generics are code-generated once for reference types, and once for every value type (or perhaps once for every value-type size, as you say). Hope someone knowledgable (hint hint) will confirm/refute/explain. :-)  
> **username_2**: The term "recompilation" has a particular meaning, that source code needs to be recompiled, for example, because a dependency on another source file where something significant (like signature) has changed.  I don't think that's the meaning you're going for however.  
> **username_2**: The .NET JIT will make multiple copies of generated code for generics for the various type parameters when they are scalar/built-in types, because that improves code performance quite a bit over boxing.  Otherwise all reference types share the same generated code, as far as I know from a few years ago.  
---
## 2 answers
### Answer by username_4 on 2024-08-06. Score: 2 | Accepted answer
I'm not sure what the official .NET implementation does these days, but from what I remember from working on an ahead-of-time compiler for C# a while back, the main constraints that limit sharing code between generic instantiations are:

(all examples assume that `T` is a generic parameter)

## Size

Two instantiations where `T` has different size will require different code for loading, storing, and copying variables of type T. Similarly, if T is used as the type of a field, other fields of the class may have different field offsets. So, instantiations for `int` and `long` will probably not share the same code. You could still share code for any method that doesn't actually need to know the size of `T`. For example, on `List.get_Size()` and `List.get_Size()` might well be able to share code, even though `List.Add()` and `List.Add()` probably wouldn't share code.

## GC Layout

If `T` is a `struct`, then, in addition to knowing the *size* of `T`, the code generator may need to know which offsets within an instance of `T` hold GC references, so that it can report all GC references to the garbage collector. So, the instantiation where `T` is a `struct` containing two `long` fields will probably not share code with the instantiation where `T` is a `struct` containing one `long` field and one `string` field, even though both `struct`s are 16 bytes in size.

## Uses of `T` without an object

There are several situations in which the type of `T` must be known at runtime. The most common are casts to type `T` (including `is` and `as`), instantiating an instance of `T` (where `T` has the `new()` constraint), and `typeof(T)`.

Unlike the "size" and "GC layout" cases above, there's a reasonably efficient way to share the code between instantiations even if type of `T` is required at runtime. If `T` is a generic parameter on a generic class `C`, the code generator can store the runtime type of `T` in the vtable of each instantiation of `C`. This allows the code for all instance methods of `C` to have access to the runtime type without bloating the size of every instance of `C`. For static methods, and for the case where `T` is a generic parameter on a generic *method*, the code generator can generate a hidden extra parameter for that method, in which the caller is expected to pass a pointer to the runtime type information for that instantiation's generic parameters.
---
### Answer by username_3 on 2024-08-07. Score: 3
When T is a ValueType (i.e a primitive or a struct), the generic will be monomorphized and there will be one implementation for that specific type (even if two types have the same behaviour for that method).
When T is not a ValueType, (an interface or a class) there is one shared method that uses the object's vtable to dispatch the appropriate calls for the type.
There is a common pattern in high-performance C# of constraining to a struct and interface that uses static types in order to force monomorphization:
interface IFoo
{
    int Transform(int a);
}

struct Bar : IFoo
{
    public int Transform(int a) => a * 2;
}

// ...

// the usage of forcing T to be a struct is this code will be monomorphized
// and `IFoo.Transform` can undergo inlining, whereas for a traditional 
// interface it will be a slow multi-dispatch call
void MapFoo(int[] arr) where T : struct, IFoo
{
    for (var i = 0; i < arr.Length; i++)
    {
        arr[i] = default(T).Transform(arr[i]);
    }
}
---
