Avertissement: Cette question est un peu hérétique... religieux programmeurs toujours en respectant les bonnes pratiques, s'il vous plaît ne pas le lire. :)
Personne ne sait pourquoi l'utilisation de TypedReference est donc déconseillée (implicitement, par manque de documentation)?
J'ai trouvé une grande utilité pour elle, comme lors du passage de paramètres génériques grâce à des fonctions qui ne devrait pas être génériques (lors de l'utilisation d'un object
peut-être exagéré ou lent, si vous avez besoin d'un type de valeur), si vous avez besoin d'un pointeur opaque, ou lorsque vous en aurez besoin pour accéder à un élément d'un tableau rapidement, dont les spécifications que vous trouvez au moment de l'exécution (à l'aide d' Array.InternalGetReference
). Depuis le CLR ne même pas permettre une utilisation incorrecte de ce type, pourquoi est-il découragé? Il ne semble pas dangereux ou quoi que ce soit...
D'autres utilisations que j'ai trouvé pour TypedReference
:
"Spécialisé" génériques en C# (c'est le type-safe):
static void foo<T>(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); }
}
Écrire du code qui fonctionne avec des pointeurs génériques (ce qui est très dangereux si mal utilisé, mais rapide et sans danger s'il est utilisé correctement):
//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<T>(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*)(&tr) = address; }
return __refvalue(tr, T);
}
L'écriture d'une méthode de version de l' sizeof
instruction, qui peut parfois être utile:
static class ArrayOfTwoElements<T> { static readonly Value = new T[2]; }
static uint SizeOf<T>()
{
unsafe
{
TypedReference
elem1 = __makeref(ArrayOfTwoElements<T>.Value[0] ),
elem2 = __makeref(ArrayOfTwoElements<T>.Value[1] );
unsafe
{ return (uint)((byte*)*(IntPtr*)(&elem2) - (byte*)*(IntPtr*)(&elem1)); }
}
}
Écrire une méthode qui passe par un "état" paramètre que l'on veut éviter de boxe:
static void call(Action<int, TypedReference> 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*/ }
}
Alors pourquoi utilise comme ceci "découragé" par le manque de documentation)? Tout particulier des raisons de sécurité? Il semble tout à fait sûr et vérifiable si il n'est pas mélangé avec des pointeurs (qui ne sont pas sûrs ou vérifiable de toute façon)...
Mise à jour:
Exemple de code pour montrer que, en effet, TypedReference
peut être deux fois plus rapide (ou plus):
using System;
using System.Collections.Generic;
static class Program
{
static void Set1<T>(T[] a, int i, int v)
{ __refvalue(__makeref(a[i]), int) = v; }
static void Set2<T>(T[] a, int i, int v)
{ a[i] = (T)(object)v; }
static void Main(string[] args)
{
var root = new List<object>();
var rand = new Random();
for (int i = 0; i < 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 < COUNT; i++)
{ Set1(arr, 0, i); }
Console.WriteLine("Using TypedReference: {0} ticks",
Environment.TickCount - start);
start = Environment.TickCount;
for (int i = 0; i < 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
}
}
(Edit: j'ai édité la référence ci-dessus, depuis la dernière version de la poste utilisé une version de débogage du code [j'ai oublié de changer pour la libération], et de mettre aucune pression sur le GC. Cette version est un peu plus réaliste, et sur mon système, c'est plus de trois fois plus vite avec TypedReference
en moyenne.)