Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>It depends... you specifically say you don't want a generic <em>class</em>... the only other option is a generic <em>method</em> in a <em>non-generic</em> class. The only <em>other</em> time you can get the compiler to emit a <code>constrained</code> call is if you call <code>ToString()</code>, <code>GetHashCode()</code> or <code>Equals()</code> (from <code>object</code>) on a <code>struct</code>, since those are then <code>constrained</code> - if the <code>struct</code> has an <code>override</code> they will be <code>call</code>; if it <em>doesn't</em> have an <code>override</code>, they will be <code>callvirt</code>. Which is why you should always <code>override</code> those 3 for any <code>struct</code> ;p But I digress. A simple example would be a utility class with some static methods - <em>extension</em> methods would be an ideal example, since you also get the advantage that the compiler will switch automatically between the public/implicit API and the extension/explicit API, without you ever needing to change code. For example, the following (which shows both an implicit and explicit implementation) has no boxing, with one <code>call</code> and one <code>constrained</code>+<code>callvirt</code>, which will implemented via <code>call</code> at the JIT:</p> <pre><code>using System; interface IFoo { void Bar(); } struct ExplicitImpl : IFoo { void IFoo.Bar() { Console.WriteLine("ExplicitImpl"); } } struct ImplicitImpl : IFoo { public void Bar() {Console.WriteLine("ImplicitImpl");} } static class FooExtensions { public static void Bar&lt;T&gt;(this T foo) where T : IFoo { foo.Bar(); } } static class Program { static void Main() { var expl = new ExplicitImpl(); expl.Bar(); // via extension method var impl = new ImplicitImpl(); impl.Bar(); // direct } } </code></pre> <p>And here's the key bits of IL:</p> <pre><code>.method private hidebysig static void Main() cil managed { .entrypoint .maxstack 1 .locals init ( [0] valuetype ExplicitImpl expl, [1] valuetype ImplicitImpl impl) L_0000: ldloca.s expl L_0002: initobj ExplicitImpl L_0008: ldloc.0 L_0009: call void FooExtensions::Bar&lt;valuetype ExplicitImpl&gt;(!!0) L_000e: ldloca.s impl L_0010: initobj ImplicitImpl L_0016: ldloca.s impl L_0018: call instance void ImplicitImpl::Bar() L_001d: ret } .method public hidebysig static void Bar&lt;(IFoo) T&gt;(!!T foo) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() .maxstack 8 L_0000: ldarga.s foo L_0002: constrained. !!T L_0008: callvirt instance void IFoo::Bar() L_000d: ret } </code></pre> <p>One downside of an extension method, though, is that it is doing an extra copy of the <code>struct</code> (see the <code>ldloc.0</code>) on the stack, which <em>might</em> be a problem if it is either oversized, or if it is a mutating method (which you should avoid anyway). If that is the case, a <code>ref</code> parameter is helpful, but note that an <em>extension</em> method cannot have a <code>ref this</code> parameter - so you can't do that with an extension method. But consider:</p> <pre><code>Bar(ref expl); Bar(ref impl); </code></pre> <p>with:</p> <pre><code>static void Bar&lt;T&gt;(ref T foo) where T : IFoo { foo.Bar(); } </code></pre> <p>which is:</p> <pre><code>L_001d: ldloca.s expl L_001f: call void Program::Bar&lt;valuetype ExplicitImpl&gt;(!!0&amp;) L_0024: ldloca.s impl L_0026: call void Program::Bar&lt;valuetype ImplicitImpl&gt;(!!0&amp;) </code></pre> <p>with:</p> <pre><code>.method private hidebysig static void Bar&lt;(IFoo) T&gt;(!!T&amp; foo) cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: constrained. !!T L_0007: callvirt instance void IFoo::Bar() L_000c: ret } </code></pre> <p>Still no boxing, but now we also never copy the struct, even for the <em>explicit</em> case.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload