Cảnh báo: Câu hỏi này hơi dị giáo ... các lập trình viên tôn giáo luôn tuân thủ các thông lệ tốt, xin đừng đọc nó. :)
Có ai biết tại sao việc sử dụng TypedReference lại bị nản lòng như vậy (ngầm, do thiếu tài liệu) không?
Tôi đã tìm thấy những công dụng tuyệt vời cho nó, chẳng hạn như khi truyền tham số chung qua các hàm không nên chung chung (khi sử dụng object
có thể quá mức hoặc chậm, nếu bạn cần một loại giá trị), khi bạn cần một con trỏ mờ hoặc khi bạn cần truy cập nhanh vào một phần tử của mảng, thông số kỹ thuật bạn tìm thấy trong thời gian chạy (sử dụng Array.InternalGetReference
). Vì CLR thậm chí không cho phép sử dụng loại này không chính xác, tại sao nó không được khuyến khích? Nó dường như không an toàn hoặc bất cứ điều gì ...
Các ứng dụng khác tôi đã tìm thấy cho TypedReference
:
Thuốc generic "Chuyên" trong C # (đây là loại an toàn):
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); }
}
Viết mã hoạt động với các con trỏ chung (điều này rất không an toàn nếu sử dụng sai, nhưng nhanh và an toàn nếu sử dụng đúng cách):
//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);
}
Viết một phiên bản phương thức của sizeof
hướng dẫn, đôi khi có thể hữu ích:
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)); }
}
}
Viết một phương thức vượt qua tham số "trạng thái" muốn tránh quyền anh:
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*/ }
}
Vậy tại sao những cách sử dụng như thế này lại "nản lòng" (do thiếu tài liệu)? Bất kỳ lý do an toàn cụ thể? Nó có vẻ hoàn toàn an toàn và có thể kiểm chứng nếu nó không bị trộn lẫn với các con trỏ (dù sao nó không an toàn hoặc có thể kiểm chứng được) ...
Cập nhật:
Mã mẫu để cho thấy rằng, thực sự, TypedReference
có thể nhanh gấp đôi (hoặc hơn):
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
}
}
(Chỉnh sửa: Tôi đã chỉnh sửa điểm chuẩn ở trên, vì phiên bản cuối cùng của bài đăng đã sử dụng phiên bản gỡ lỗi của mã [Tôi quên thay đổi nó để phát hành] và không gây áp lực lên GC. Phiên bản này thực tế hơn một chút, và trên hệ thống của tôi, TypedReference
trung bình nhanh hơn gấp ba lần .)
int
-> DockStyle
). Hộp này là thật, và chậm hơn gần mười lần.
TypedReference: 203 ticks
,boxing/unboxing: 31 ticks
. Bất kể tôi cố gắng gì (bao gồm cả những cách khác nhau để thực hiện thời gian), quyền anh / unboxing vẫn nhanh hơn trên hệ thống của tôi.