Uyarı: Bu soru biraz sapkın ... Dindar programcılar her zaman iyi uygulamalara bağlı kalıyorlar, lütfen okumayın. :)
TypedReference kullanımının neden bu kadar cesaret kırıldığını bilen var mı (örtük olarak, belge eksikliği nedeniyle)?
object
Opak bir işaretçiye ihtiyaç duyduğunuzda, genel parametreleri genel olmaması gereken işlevlerden geçirirken ( bir değer türüne ihtiyacınız varsa aşırı veya yavaş olabilir) , bunun için harika kullanım alanları buldum veya bir dizinin bir öğesine hızlı bir şekilde erişmeniz gerektiğinde, özelliklerini çalışma zamanında bulduğunuzda (kullanarak Array.InternalGetReference
). CLR bu türün yanlış kullanımına bile izin vermediğine göre, neden önerilmiyor? Güvensiz falan görünmüyor ...
Bulduğum diğer kullanımlar TypedReference
:
C # 'da "özelleştirme" jenerikleri (bu tür açısından güvenlidir):
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); }
}
Genel işaretçilerle çalışan kod yazma (bu, yanlış kullanıldığında çok güvensiz, ancak doğru kullanıldığında hızlı ve güvenlidir):
//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);
}
Bazen yararlı olabilecek talimatın bir yöntem versiyonunun yazılması sizeof
:
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)); }
}
}
Kutudan kaçınmak isteyen bir "durum" parametresini geçiren bir yöntem yazma:
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*/ }
}
Öyleyse neden bu tür kullanımlar "önerilmiyor" (belge eksikliği nedeniyle)? Herhangi bir özel güvenlik nedeni var mı? İşaretçilerle karıştırılmadıysa (zaten güvenli veya doğrulanamaz) tamamen güvenli ve doğrulanabilir görünüyor ...
Güncelleme:
Gerçekten TypedReference
iki kat daha hızlı (veya daha fazla) olabileceğini gösteren örnek kod :
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
}
}
(Düzenleme: Gönderinin son sürümü kodun hata ayıklama sürümünü kullandığından [yayınlamak için değiştirmeyi unuttum] ve GC üzerinde hiçbir baskı oluşturmadığından yukarıdaki karşılaştırmayı düzenledim. Bu sürüm biraz daha gerçekçi ve sistemimde TypedReference
ortalamada üç kattan daha hızlı .)
int
-> DockStyle
) temel bir tür yayınlama . Bu kutular gerçektir ve neredeyse on kat daha yavaştır.
TypedReference: 203 ticks
,boxing/unboxing: 31 ticks
. Ne denersem deneyeyim (zamanlamayı yapmanın farklı yolları dahil) sistemimde boks / kutudan çıkarma işlemi hala daha hızlı.