Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy is TypedReference behind the scenes? It's so fast and safe... almost magical!
    primarykey
    data
    text
    <p>Warning: This question is a bit heretical... religious programmers always abiding by good practices, please don't read it. :)</p> <p>Does anyone know why the use of <a href="http://msdn.microsoft.com/en-us/library/system.typedreference%28v=VS.80%29.aspx" rel="noreferrer">TypedReference</a> is so discouraged (implicitly, by lack of documentation)?</p> <p>I've found great uses for it, such as when passing generic parameters through functions that shouldn't be generic (when using an <code>object</code> might be overkill or slow, if you need a value type), for when you need an opaque pointer, or for when you need to access an element of an array quickly, whose specs you find at runtime (using <code>Array.InternalGetReference</code>). Since the CLR doesn't even allow incorrect usage of this type, why is it discouraged? It doesn't seem to be unsafe or anything...</p> <hr> <p>Other uses I've found for <code>TypedReference</code>:</p> <p>"Specializing" generics in C# (this is type-safe):</p> <pre><code>static void foo&lt;T&gt;(ref T value) { //This is the ONLY way to treat value as int, without boxing/unboxing objects if (value is int) { __refvalue(__makeref(value), int) = 1; } else { value = default(T); } } </code></pre> <p>Writing code that works with generic pointers (this is <em>very</em> unsafe if misused, but fast and safe if used correctly):</p> <pre><code>//This bypasses the restriction that you can't have a pointer to T, //letting you write very high-performance generic code. //It's dangerous if you don't know what you're doing, but very worth if you do. static T Read&lt;T&gt;(IntPtr address) { var obj = default(T); var tr = __makeref(obj); //This is equivalent to shooting yourself in the foot //but it's the only high-perf solution in some cases //it sets the first field of the TypedReference (which is a pointer) //to the address you give it, then it dereferences the value. //Better be 10000% sure that your type T is unmanaged/blittable... unsafe { *(IntPtr*)(&amp;tr) = address; } return __refvalue(tr, T); } </code></pre> <p>Writing a <em>method</em> version of the <code>sizeof</code> instruction, which can be occasionally useful:</p> <pre><code>static class ArrayOfTwoElements&lt;T&gt; { static readonly Value = new T[2]; } static uint SizeOf&lt;T&gt;() { unsafe { TypedReference elem1 = __makeref(ArrayOfTwoElements&lt;T&gt;.Value[0] ), elem2 = __makeref(ArrayOfTwoElements&lt;T&gt;.Value[1] ); unsafe { return (uint)((byte*)*(IntPtr*)(&amp;elem2) - (byte*)*(IntPtr*)(&amp;elem1)); } } } </code></pre> <p>Writing a method that passes a "state" parameter that wants to avoid boxing:</p> <pre><code>static void call(Action&lt;int, TypedReference&gt; action, TypedReference state) { //Note: I could've said "object" instead of "TypedReference", //but if I had, then the user would've had to box any value types try { action(0, state); } finally { /*Do any cleanup needed*/ } } </code></pre> <p>So why are uses like this "discouraged" (by lack of documentation)? Any particular safety reasons? It seems perfectly safe and verifiable if it's not mixed with pointers (which aren't safe or verifiable anyway)...</p> <hr> <p>Update:</p> <p>Sample code to show that, indeed, <code>TypedReference</code> can be twice as fast (or more):</p> <pre><code>using System; using System.Collections.Generic; static class Program { static void Set1&lt;T&gt;(T[] a, int i, int v) { __refvalue(__makeref(a[i]), int) = v; } static void Set2&lt;T&gt;(T[] a, int i, int v) { a[i] = (T)(object)v; } static void Main(string[] args) { var root = new List&lt;object&gt;(); var rand = new Random(); for (int i = 0; i &lt; 1024; i++) { root.Add(new byte[rand.Next(1024 * 64)]); } //The above code is to put just a bit of pressure on the GC var arr = new int[5]; int start; const int COUNT = 40000000; start = Environment.TickCount; for (int i = 0; i &lt; COUNT; i++) { Set1(arr, 0, i); } Console.WriteLine("Using TypedReference: {0} ticks", Environment.TickCount - start); start = Environment.TickCount; for (int i = 0; i &lt; COUNT; i++) { Set2(arr, 0, i); } Console.WriteLine("Using boxing/unboxing: {0} ticks", Environment.TickCount - start); //Output Using TypedReference: 156 ticks //Output Using boxing/unboxing: 484 ticks } } </code></pre> <p>(Edit: I edited the benchmark above, since the last version of the post used a debug version of the code [I forgot to change it to release], and put no pressure on the GC. This version is a bit more realistic, and on my system, it's more than three times faster with <code>TypedReference</code> on average.)</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.
 

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