为什么 TypedReference 是在幕后?它是如此快速和安全......几乎是神奇的!

2023-11-23

警告:这个问题有点异端……宗教程序员总是遵守良好的实践,请不要阅读它。 :)

有谁知道为什么使用类型化参考如此沮丧(隐含地,由于缺乏文档)?

我发现它有很大的用途,例如当通过不应该是泛型的函数传递泛型参数时(当使用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); }
}

编写使用通用指针的代码(这是very如果使用不当则不安全,但如果使用正确则快速且安全):

//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);
}

写一个method的版本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*/ }
}

那么为什么这样的使用会被“劝阻”(由于缺乏文档)?有什么特别的安全原因吗?如果它不与指针混合(无论如何都不安全或不可验证),它似乎是完全安全和可验证的......


Update:

示例代码确实表明,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一般。)


简短回答:可移植性.

While __arglist, __makeref, and __refvalue are 语言扩展并且在 C# 语言规范中没有记录,用于在幕后实现它们的构造(vararg调用约定,TypedReference type, arglist, refanytype, mkanyref, and refanyval说明)被完美地记录在CLI 规范 (ECMA-335) in the 可变参数库.

在 Vararg 库中的定义非常清楚地表明它们主要是为了支持可变长度参数列表,除此之外别无其他。变量参数列表在不需要与使用可变参数的外部 C 代码交互的平台中几乎没有用处。因此,Varargs 库不是任何 CLI 配置文件的一部分。合法的 CLI 实现可能会选择不支持 Varargs 库,因为它不包含在 CLI 内核配置文件中:

4.1.6 可变参数

The 可变参数功能集支持可变长度参数列表和运行时类型指针。

如果省略:任何引用方法的尝试vararg调用约定或与 vararg 方法关联的签名编码(请参阅分区 II)应抛出System.NotImplementedException例外。使用 CIL 指令的方法arglist, refanytype, mkrefany, and refanyval将抛出System.NotImplementedException例外。没有指定例外的具体时间。方式System.TypedReference不需要定义。

更新(回复GetValueDirect评论):

FieldInfo.GetValueDirect are FieldInfo.SetValueDirect are not基类库的一部分。请注意,.NET Framework 类库和基类库之间存在差异。 BCL 是 CLI/C# 的一致实现所需的唯一内容,并记录在ECMA TR/84。 (实际上,FieldInfo它本身是 Reflection 库的一部分,也不包含在 CLI 内核配置文件中)。

一旦您使用 BCL 之外的方法,您就会放弃一点可移植性(随着 Silverlight 和 MonoTouch 等非 .NET CLI 实现的出现,这一点变得越来越重要)。即使某个实现想要提高与 Microsoft .NET Framework 类库的兼容性,它也可以简单地提供GetValueDirect and SetValueDirect采取TypedReference没有使TypedReference由运行时专门处理(基本上,使它们等同于它们的object没有性能优势的同行)。

如果他们用 C# 记录它,它至少会产生一些影响:

  1. 与任何功能一样,它may成为新功能的障碍,特别是因为这个功能并不真正适合 C# 的设计,并且需要奇怪的语法扩展和运行时对类型的特殊处理。
  2. C# 的所有实现都必须以某种方式实现此功能,对于根本不在 CLI 之上运行或在没有 Varargs 的 CLI 之上运行的 C# 实现来说,这不一定是微不足道的/可能的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 TypedReference 是在幕后?它是如此快速和安全......几乎是神奇的! 的相关文章

随机推荐

  • 重置 svg 填充 css

    我想让所有 svgs 都具有相同的纯色 所以我用 svg fill ccc 但我想在 hover 上获得默认填充 如何禁用填充并恢复默认值 您可以使用以下方法执行此操作 not 并有效地设置 不悬停 的样式 svg not hover fi
  • Jupyter:安装后没有名为“imblearn”的模块

    我在 ANACONDA Navigator 上安装了 imbalanced learn 版本 0 3 1 当我使用 Jupyter Python 3 运行不平衡学习网站上的示例时 from imblearn datasets import
  • Git版本兼容性

    使用 Git 进行版本控制 与不同版本的 Git 协同工作的效果如何 有关的体验 好还是坏 是什么 长话短说 我正在考虑将 Git 用于一些计划的家庭项目 但由于我使用存储库中的默认包进行的大杂烩设置将意味着完全不同的版本 我计划在运行 U
  • 关于 C# 变量作用域与其他语言的问题

    首先声明一下 我以前没用过C 对它了解不多 我正在学习 Sebesta 的 编程语言概念第 9 版 一书 准备 编程语言 考试 当我读到以下摘录自 范围声明顺序 第246页 时 我有点困惑 例如 在 C99 C Java 中 所有局部变量的
  • 没有RTTI的shared_ptr?

    我正在尝试使用shared ptr在使用 xc32 1 34 gcc 4 5 2 的衍生版本 构建的嵌入式项目中 该项目已禁用 RTTI fno rtti include
  • 如何在 MVC3 中从 javascript 调用控制器方法?

    我使用 MVC3 架构 c net 当焦点更改到下一个字段 即密码字段 时 我需要立即将文本框内容 用户 ID 与数据库进行比较 所以我想对 User Id 字段使用 onblur 事件 然后调用 Controller 方法 谁能告诉我如何
  • 通过套接字发送和接收数组

    是否可以使用Python通过UDP套接字发送数组 我正在使用 Python 2 5 并尝试发送一个简单的数组 但它不起作用 它可以成功发送数组 但是当我尝试使用数组的一项来打印它时 程序崩溃了 我不确定错误是什么 因为我采取了预防措施将数据
  • 使用 psycopg2 将 PostgreSQL UUID 数组作为列表返回

    我有一个 SQL 语句 其中包含嵌入在ARRAY 像这样 SELECT foo ARRAY SELECT x from y AS bar 查询工作正常 但是在 psycopg2 结果游标中 数组作为字符串返回 如 1 2 3 而不是列表 我
  • CQL SELECT 大于索引非键列上的查询

    EDIT1 在原始问题之后添加了一个案例来描述问题 我希望查询不属于我的键的列 如果我理解正确的话 我需要在该列上定义一个二级索引 但是 我希望使用大于条件 不仅仅是相等条件 但这似乎仍然不受支持 我错过了什么吗 您将如何解决这个问题 我想
  • 使数组严格递增所需的最少更改

    我遇到一个问题 我们有一个正数数组 我们必须通过对数组元素进行零次或多次更改来使其严格增加 我们被问到使数组严格递增所需的最少更改次数 Example 如果数组是 1 2 9 10 3 15 因此 如果将 3 更改为 12 到 14 之间的
  • 打字稿中的 lodash _.get 函数

    经过一番谷歌搜索后 我感觉很多 lodash 的功能都可以使用本机打字稿来实现 但我找不到 get 函数的直接答案 在lodash中 使用 get函数警告1 let obj a b 1 let a get obj a b alert a 有
  • 权威 C++ 书籍指南和列表

    这个问题的答案是社区努力 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 这个问题试图从每年出版的数十本糟糕的 C 书籍中收集少数珍珠 与许多其他编程语言 通常是从 Internet 上的教程中学习 不同 很少有人能够在不学习一本写得
  • Javascript数字连接而不是相加,但typeof是数字而不是字符串

    我是 JavaScript 新手 我正在构建一个计算器here 我已将输入值存储在变量中 以便最终可以操纵结果以根据输入执行计算 现在我只想将所有值加在一起 然而 它们不是相加 而是连接 我使用 parseInt 来防止 javascrip
  • 我可以监控线程消息队列的大小吗?

    我们的应用程序从 DCOM 收到系统调用失败 RPC 错误 0x80010100 我们怀疑目标线程的消息队列已满 尽管我不相信这是真的 我知道队列是仅限于10 000消息我想看看在常见情况下我们是否接近这个数字 有没有办法监控线程消息队列的
  • Google Play - 未发布应用程序的下载链接?

    是否可以提供 Google Play 中未发布的应用程序的下载链接 我想在发布之前将其发送给一些人 我知道我可以只发送 APK 但我想先上演它 Thanks 是的 可以直接从 URL 下载应用程序 亚马逊应用程序商店就是以这种方式分发的 唯
  • 超时重新连接MySQL

    我有一个 Python 程序 它在后台运行数周 并且每隔一段时间就会进行数据库查询 为此 我使用 ORMpeewee 版本 2 2 1 我在用MySQL作为后端 最近 我在访问数据库时经常遇到问题 通常是在运行程序几天后 引发的错误peew
  • 自动回显 IPython 中赋值语句的结果 [重复]

    这个问题在这里已经有答案了 有没有办法让 IPython 自动回显赋值语句的结果 例如 在MATLAB 结束不带分号的赋值语句会打印赋值结果 而在语句末尾放置分号会抑制任何输出 gt gt b 1 2 b 3 gt gt b 1 2 gt
  • 在 Python 中口头格式化数字

    pythonistas 如何将数字打印为单词 就像 Common Lisp 代码的等价物 3 gt format t r 1e25 nine septillion nine hundred and ninety nine sextillio
  • Hadoop Yarn:如何使用 Spark 限制资源的动态自分配?

    在我们在 Yarn 下运行的 Hadoop 集群中 我们遇到了一个问题 一些 聪明 的人能够通过在 pySpark Jupyter 笔记本中配置 Spark 作业来消耗更大的资源块 例如 conf SparkConf setAppName
  • 为什么 TypedReference 是在幕后?它是如此快速和安全......几乎是神奇的!

    警告 这个问题有点异端 宗教程序员总是遵守良好的实践 请不要阅读它 有谁知道为什么使用类型化参考如此沮丧 隐含地 由于缺乏文档 我发现它有很大的用途 例如当通过不应该是泛型的函数传递泛型参数时 当使用object如果您需要值类型 则可能会过