- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
警告:这个问题有点邪门……信教的程序员一向恪守优良作法,请勿阅读。 :)
有谁知道为什么使用TypedReference如此气馁(隐含地,由于缺乏文档)?
我发现它有很好的用途,例如当通过不应该是通用的函数传递通用参数时(如果您需要一个值类型,使用 object
可能会过大或缓慢),当您需要一个不透明的指针时,或者当您需要快速访问数组元素时,您可以在运行时找到其规范(使用 Array.InternalGetReference
)。既然 CLR 甚至不允许不正确地使用这种类型,为什么不鼓励这样做呢?好像没有什么不安全之类的……
我发现 TypedReference
的其他用途:
C# 中的“特化”泛型(这是类型安全的):
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); }
}
编写使用通用指针的代码(如果使用不当,非常不安全,但如果使用正确,则快速且安全):
//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);
}
编写 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)); }
}
}
编写一个传递“状态”参数的方法,希望避免装箱:
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*/ }
}
那么为什么这样的使用“不被鼓励”(由于缺乏文档)?有什么特别的安全原因吗?如果它不与指针混合(无论如何都不安全或不可验证),它似乎是完全安全和可验证的......
更新:
显示 TypedReference
确实可以快两倍(或更多)的示例代码:
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
}
}
(编辑:我编辑了上面的基准测试,因为帖子的最后一个版本使用了代码的调试版本[我忘记将其更改为发布],并且对GC没有压力。这个版本有点多现实,并且在我的系统上,使用 TypedReference
平均要快三倍以上。)
最佳答案
简短回答:可移植性。
虽然 __arglist
、__makeref
和 __refvalue
是语言扩展并且未记录在 C# 语言规范中,用于在幕后实现它们的构造(vararg
调用约定、TypedReference
类型、arglist
、refanytype
、mkanyref
和 refanyval
指令)完美记录在 CLI Specification (ECMA-335) 中在 Vararg 库中。
在 Vararg 库中定义很清楚,它们主要用于支持可变长度参数列表,仅此而已。可变参数列表在不需要与使用可变参数的外部 C 代码交互的平台中几乎没有用处。因此,Varargs 库不是任何 CLI 配置文件的一部分。合法的 CLI 实现可能会选择不支持 Varargs 库,因为它不包含在 CLI 内核配置文件中:
4.1.6 Vararg
The vararg feature set supports variable-length argument lists and runtime-typed pointers.
If omitted: Any attempt to reference a method with the
vararg
calling convention or the signature encodings associated with vararg methods (see Partition II) shall throw theSystem.NotImplementedException
exception. Methods using the CIL instructionsarglist
,refanytype
,mkrefany
, andrefanyval
shall throw theSystem.NotImplementedException
exception. The precise timing of the exception is not specified. The typeSystem.TypedReference
need not be defined.
GetValueDirect
评论):FieldInfo.GetValueDirect
是 FieldInfo.SetValueDirect
不是 基类库的一部分。请注意,.NET Framework 类库和基类库之间存在差异。 BCL 是 CLI/C# 的一致性实现所需的唯一东西,记录在 ECMA TR/84 中。 . (事实上,FieldInfo
本身是 Reflection 库的一部分,它也不包含在 CLI 内核配置文件中)。
一旦您使用 BCL 之外的方法,您就放弃了一点可移植性(随着 Silverlight 和 MonoTouch 等非 .NET CLI 实现的出现,这一点变得越来越重要)。即使实现想要提高与 Microsoft .NET Framework 类库的兼容性,它也可以简单地提供 GetValueDirect
和 SetValueDirect
获取 TypedReference
而无需进行由运行时专门处理的 TypedReference
(基本上,使它们等同于它们的 object
对应物,但没有性能优势)。
如果他们用 C# 记录它,它至少会产生一些影响:
关于c# - 为什么 TypedReference 在幕后?它是如此快速和安全......几乎是神奇的!,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4764573/
tty_driver 结构中的“神奇”值是什么 struct tty_driver { int magic; /* magic number for this stru
这是一个等效的提取代码: #include #include #include #include #include class ChatMessageEdit : public QTextE
我还没有找到适合我的这个问题的具体答案,但也许我误解了一两个关键点。 我正在尝试为一个项目创建数据迁移策略,其中 3 个系统(2 个 MySQL、1 个 MS SQL)将合并到 1 个新系统 (MS
我想在输出 JSON 时从 ActiveRecord/ActiveModel 类中过滤掉特定字段。 最直接的方法就是覆盖 as_json,可能像这样: def as_json (options = n
我是一名优秀的程序员,十分优秀!