为什么TypedReference在幕后?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (80)

我发现的其他用途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)); }
    }
}

编写一个传递“state”参数的方法,以避免装箱:

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
    }
}
提问于
用户回答回答于

__arglist__makeref,和__refvalue语言扩展在C#语言规范中是没有文档的,这些构造用于在引擎盖下实现它们(vararg电话会议,TypedReference类型,arglistrefanytypemkanyref,和refanyva

在VarargLibrary中定义它可以很清楚地表明,它们主要是为了支持可变长度的参数列表,而不是其他的。变量参数列表在不需要与使用varargs的外部C代码接口的平台上没有什么用处。因此,Varargs库不是任何CLI配置文件的一部分。合法的CLI实现可能选择不支持Varargs库,因为它不包含在CLI内核配置文件中:

4.1.6 Vararg 大vararg特征集支持可变长度的参数列表和运行时类型的指针。undefined如果省略:对象引用方法的任何尝试。vararg调用约定或与vararg方法关联的签名编码(请参阅PartitionII)将引发System.NotImplementedException例外。方法使用CIL指令。arglistrefanytypemkrefany,和refanyval将抛出System.NotImplementedException例外。未指定异常的确切时间。类型System.TypedReference不需要定义。

最新情况(回复)GetValueDirect评论):

FieldInfo.GetValueDirectFieldInfo.SetValueDirect基类库的一部分。请注意,.NET Framework类库与基类库之间存在差异。BCL是CLI/C#的一致性实现所需的唯一条件。

一旦使用bcl之外的方法,就会放弃一些可移植性(随着Silverlight和MonoTouch等非.NET CLI实现的出现,这一点变得越来越重要)。即使实现想要增强与Microsoft.NET框架类库的兼容性,它也可以简单地提供GetValueDirectSetValueDirect带着...TypedReference不做TypedReference由运行时特别处理(基本上,使它们与它们的object没有性能效益的对应方)。

如果他们把它记录在C#中,它至少会有几个暗示:

  1. 就像任何一个特征,它五月成为新特性的障碍,特别是因为这个特性并不适合C#的设计,并且需要奇怪的语法扩展和运行时对类型的特殊处理。
  2. C#的所有实现都必须以某种方式实现这个特性,对于不运行在CLI之上或在没有Varargs的CLI之上运行的C#实现来说,这不一定是简单的/可能的。
用户回答回答于

实际上,诸如指针这样的特性,stackalloc,并且在某些情况下存在某些优化的框架函数以提高性能。

我会说初等类型安全的好处,也提高性能类似于Typedeference通过避免拳击和取消拳击。

static void call(Action<int, TypedReference> action, TypedReference state){
    action(0, state);
}

对此:

static void call<T>(Action<int, T> action, T state){
    action(0, state);
}

扫码关注云+社区