动态生成的 IL 中的值类型转换

2024-03-07

Update
一年多后,我终于意识到了这种行为的原因。 本质上,一个对象不能被拆箱为与它不同的类型 被装箱为(即使该类型强制转换或转换为目标 类型),如果你不知道正确的类型,你必须发现它 不知何故。作业可能完全有效,但不可行 让这一切自动发生。

例如,即使一个字节适合 Int64,您也无法拆箱 字节为长整型。您必须将一个字节拆箱为一个字节,然后进行转换。

如果您没有足够的信息来执行此操作,则必须使用其他方法(如下所示)。

代表和身份 http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx

原始问题

我正在与 IL 合作来提高许多通常通过反射处理的任务的性能。为了实现这一点,我大量使用DynamicMethod class.

我编写了用于设置对象属性的动态方法。这允许开发人员仅根据名称动态设置属性。这对于将记录从数据库加载到业务对象等任务非常有用。

然而,我被困在一件(可能很简单)的事情上:将值类型,甚至更大的类型转换为更小的类型(例如将字节的值放入 Int32 中)。

这是我用来创建动态属性设置器的方法。请注意,我已删除除 IL 生成部分之外的所有内容。

 // An "Entity" is simply a base class for objects which use these dynamic methods.
 // Thus, this dynamic method takes an Entity as an argument and an object value
 DynamicMethod method = new DynamicMethod( string.Empty, null, new Type[] { typeof( Entity ), typeof( object ) } );

ILGenerator il = method.GetILGenerator();    
PropertyInfo pi = entityType.GetProperty( propertyName );
MethodInfo mi = pi.GetSetMethod();

il.Emit( OpCodes.Ldarg_0 ); // push entity
il.Emit( OpCodes.Castclass, entityType ); // cast entity
il.Emit( OpCodes.Ldarg_1 ); // push value

if( propertyType.IsValueType )
{
    il.Emit( OpCodes.Unbox_Any, propertyType );
    // type conversion should go here?
}
else
{
    il.Emit( OpCodes.Castclass, propertyType ); // cast value
}

//
// The following Callvirt works only if the source and destination types are exactly the same
il.Emit( OpCodes.Callvirt, mi ); // call the appropriate setter method
il.Emit( OpCodes.Ret );

我尝试在 IL 生成时检查属性类型并使用转换OpCodes。尽管如此,代码仍然抛出一个InvalidCastException。这个示例显示了一个检查(我认为)应该确保堆栈上的任何值都被转换为匹配它所分配到的属性的类型。

if( pi.PropertyType == typeof( long ) )
{
    il.Emit( OpCodes.Conv_I8 );
}
else if( pi.PropertyType == typeof( int ) )
{
    il.Emit( OpCodes.Conv_I4 );
}
else if( pi.PropertyType == typeof( short ) )
{
    il.Emit( OpCodes.Conv_I2 );
}
else if( pi.PropertyType == typeof( byte ) )
{
    il.Emit( OpCodes.Conv_I1 );
}

我还尝试在拆箱值类型之前或之后进行转换,例如:

if( propertyType.IsValueType )
{
    // cast here?
    il.Emit( OpCodes.Unbox_Any, propertyType );
    // or here?
}

我想我可以创建 IL 来动态创建Convert对象和调用ChangeType()但这似乎很浪费,因为大多数时候这甚至不是问题(当类型匹配时,就没有问题)。

总结一下问题:当我将值类型传递给动态生成的方法时,如果它与分配给它的属性类型不完全匹配,则会引发 InvalidCastException,即使目标类型的大小大于源类型也是如此。我尝试过的类型转换不起作用。

如果您需要更多信息来回答问题,请告诉我。

编辑:@JeffN825 在考虑转换方面走在正确的轨道上。我曾考虑过 System.Convert 类,但由于成本太高而排除了它。但是,有了目标类型,您就可以创建一个仅调用适合该类型的方法的例程。这(基于测试)似乎相对便宜。结果代码看起来像这样:

il.Emit( OpCodes.Call, GetConvertMethod( propertyType );

internal static MethodInfo GetConvertMethod( Type targetType )
{
    string name;

    if( targetType == typeof( bool ) )
    {
        name = "ToBoolean";
    }
    else if( targetType == typeof( byte ) )
    {
        name = "ToByte";
    }
    else if( targetType == typeof( short ) )
    {
        name = "ToInt16";
    }
    else if( targetType == typeof( int ) )
    {
        name = "ToInt32";
    }
    else if( targetType == typeof( long ) )
    {
        name = "ToInt64";
    }
    else
    {
        throw new NotImplementedException( string.Format( "Conversion to {0} is not implemented.", targetType.Name ) );
    }

    return typeof( Convert ).GetMethod( name, BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof( object ) }, null );
}

诚然,这会导致一个巨大的 if/else 语句(当所有类型都实现时),但它与 BCL 的作用没有什么不同,并且仅在生成 IL 时才执行此检查,并且not每次通话。因此,它选择正确的 Convert 方法并编译对其的调用。

注意OpCodes.Call是必需的,而不是OpCodes.Callvirt,作为Convert对象的方法是静态的。

表现令人尊敬;临时测试显示,对动态生成的 set 方法进行 1,000,000 次调用大约需要 40 毫秒。击败反思。


我知道这并不能直接回答您的问题,但是在维护许多不同的 IL 生成实现之后,我发现使用表达式树取得了更好的成功。

它们可作为 .NET 2.0/3.5 的 DLR 的一部分提供,或直接集成在 .NET 4.0 中。

您可以将表达式树编译为 lambda 或将事件直接发送到DynamicMethod.

最终,底层表达式树 API 使用相同的方法生成 ILILGenerator机制。

附:当我像这样调试 IL 生成时,我喜欢创建一个简单的控制台测试应用程序并反射编译后的代码。
针对您的问题,我尝试了以下方法:

static class Program
{
    static void Main(string[] args)
    {
        DoIt((byte) 0);
    }

    static void DoIt(object value)
    {
        Entity e = new Entity();
        e.Value = (int)value;
    }
}

public class Entity
{
    public int Value { get; set; }
}

生成的IL是:

L_0000: nop 
L_0001: newobj instance void ConsoleApplication2.Entity::.ctor()
L_0006: stloc.0 
L_0007: ldloc.0 
L_0008: ldarg.0 
L_0009: unbox.any int32
L_000e: callvirt instance void ConsoleApplication2.Entity::set_Value(int32)
L_0013: nop 
L_0014: ret 

就像您一样,它正在对值类型进行拆箱。你猜怎么了?我收到无效的强制转换异常!所以问题不在于您生成的 IL。我建议您尝试将其用作 IConvertable:

static void DoIt(object value)
{
    Entity e = new Entity();
    e.Value = ((IConvertible) value).ToInt32(null);
}

L_0000: nop 
L_0001: newobj instance void ConsoleApplication2.Entity::.ctor()
L_0006: stloc.0 
L_0007: ldloc.0 
L_0008: ldarg.0 
L_0009: castclass [mscorlib]System.IConvertible
L_000e: ldnull 
L_000f: callvirt instance int32 [mscorlib]System.IConvertible::ToInt32(class [mscorlib]System.IFormatProvider)
L_0014: callvirt instance void ConsoleApplication2.Entity::set_Value(int32)
L_0019: nop 
L_001a: ret 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态生成的 IL 中的值类型转换 的相关文章

  • WPF 中的屏幕分辨率问题?

    我将在 WPF 中使用以下代码检测分辨率 double height System Windows SystemParameters PrimaryScreenHeight double width System Windows Syste
  • 如何使用 zlib 制作 .zip 文件

    我正在阅读zlib的文档 它相当详细 但我读到了这一行 输出数据将位于zlib格式 与 gzip 或zip formats http www zlib net zlib how html http www zlib net zlib how
  • 分段错误(核心转储)错误

    我的程序编译罚款 但在输入文件时出现 分段错误 核心转储 错误 我没有正确处理 ostream 吗 include
  • 内联函数/方法

    声明 内联函数必须在调用之前定义 这个说法正确吗 EDIT 该问题最初是德语 内联功能穆森 弗 伊赫雷姆 奥夫鲁夫定义 sein 也许它对任何人都有帮助 是的 它是正确的 但只是部分正确 它可能正确地重新构建如下 内联函数必须在每个翻译单位
  • 将字节数组转换为托管结构

    更新 这个问题的答案帮助我编写了开源项目GitHub 上的 AlicanC 现代战争 2 工具 https github com AlicanC AlicanC s Modern Warfare 2 Tool 你可以看到我是如何阅读这些数据
  • C 程序从连接到系统的 USB 设备读取数据

    我正在尝试从连接到系统 USB 端口的 USB 设备 例如随身碟 获取数据 在这里 我可以打开设备文件并读取一些随机原始数据 但我想获取像 minicom teraterm 这样的数据 请让我知道我可以使用哪些方法和库来成功完成此操作以及如
  • 从多线程程序中调用 system()

    我们正在开发一个用 C 编写的多线程内存消耗应用程序 我们必须执行大量的 shellscript linux 命令 并获取返回码 读完之后article http www linuxprogrammingblog com threads a
  • 在 C++ 中将成对向量转换为两个独立向量的最快方法

    假设我有一个vector of pair
  • 将字符串中的“奇怪”字符转换为罗马字符

    我需要能够将用户输入仅转换为 a z 罗马字符 不区分大小写 所以 我感兴趣的角色只有26个 然而 用户可以输入他们想要的任何 形式 的字符 西班牙语 n 法语 e 和德语 u 都可以包含用户输入中的重音符号 这些重音符号会被程序删除 我已
  • 如何创建用于 QML 的通用对象模型?

    我想知道是否有任何宏或方法如何将 Qt 模型注册为 QObject 的属性 例如 我有AnimalModel http doc qt io qt 5 qtquick modelviewsdata cppmodels html qabstra
  • 如何将带有自定义分配器的 std::vector 传递给需要带有 std::allocator 的函数?

    我正在使用外部库 pcl 因此我需要一个不会更改现有函数原型的解决方案 我正在使用的一个函数生成一个std vector
  • 两种类型的回发事件

    1 我发现了两篇文章 每篇文章对两种类型的回发事件的分类都略有不同 一位资源说两种类型的回发事件是Changed事件 其中控件实现 IPostbackDataHandler 当数据在回发之间更改时触发 然后Raised事件 其中控件实现 I
  • C++ 插件的“最适合”动态类型匹配

    我有一个几乎所有东西都是插件的架构 该架构以图形用户界面为基础 其中每个插件都由一个 表面 即用户可以通过其与插件交互的 UI 控件 表示 这些表面也是插件 每当添加新插件时 瘦主机都会自动确定哪个可用表面与其最匹配的 UI 如何在 C 中
  • 使用 WF 的多线程应用程序的错误处理模式?

    我正在写一个又长又详细的问题 但只是放弃了它 转而选择一个更简单的问题 但我在这里找不到答案 应用程序简要说明 我有一个 WPF 应用程序 它生成多个线程 每个线程执行自己的 WF 处理线程和 WF 中的错误 允许用户从 GUI 端进行交互
  • 默认析构函数做了多少事情

    C 类中的默认析构函数是否会自动删除代码中未显式分配的成员 例如 class C public C int arr 100 int main void C myC new C delete myC return 0 删除 myC 会自动释放
  • WPF。如何从另一个窗口隐藏/显示主窗口

    我有两个窗口 MainWindow 和 Login 显示登录的按钮位于主窗口 this Hide Login li new Login li Show 登录窗口上有一个检查密码的按钮 如果密码正确 我如何显示主窗口 将参数传递给 MainW
  • 使用 HTMLAgilityPack 从节点的子节点中选择所有

    我有以下代码用于获取 html 页面 将网址设置为绝对 然后将链接设置为 rel nofollow 并在新窗口 选项卡中打开 我的问题是关于将属性添加到 a s string url http www mysite com string s
  • C 中带有指针的结构的内存开销[重复]

    这个问题在这里已经有答案了 我意识到当我的结构包含指针时 它们会产生内存开销 这里有一个例子 typedef struct int num1 int num2 myStruct1 typedef struct int p int num2
  • 在 C++17 中使用 成员的链接错误

    我在 Ubuntu 16 04 上使用 gcc 7 2 并且需要使用 C 17 中的新文件系统库 尽管确实有一个名为experimental filesystem的库 但我无法使用它的任何成员 例如 当我尝试编译此文件时 include
  • 受限 AppDomain 中的代码访问安全异常

    Goal 我需要在权限非常有限的 AppDomain 中运行一些代码 它不应该访问任何花哨或不安全的内容 except对于我在其他地方定义的一些辅助方法 我做了什么 我正在创建一个具有所需基本权限的沙箱 AppDomain 并创建一个运行代

随机推荐

  • 给定一个完全由字符串元组列表表示的线性顺序,将该顺序输出为字符串列表

    给定形式的项目对 a b where a b means a gt b 例如 best better best good better good 我想输出一个表单列表 best better good 由于某种原因 这非常困难 有什么想法吗
  • 如何使用node.js基于id(transectionid)组合两个集合?

    我有横切和购买集合 其中包含横切和购买详细信息 现在我想将其转换为单个集合 基于横断面 我们需要合并文档 以下是我的横断面收集数据 transectionid 1 transectionamount 2000 transectiondate
  • 如何从备份文件中检索数据库的逻辑文件名

    我正在研究如何的步骤使用 SQL 脚本 T SQL 恢复数据库备份 http blog sqlauthority com 2007 02 25 sql server restore database backup using sql scr
  • 如何确定 JNI 全局引用内存泄漏的原因?

    我正在使用 Tomcat 停止我的 Web 应用程序后仍然存在对类加载器的引用我的网络应用程序的实例 结果是大量内存 主要与静态数据相关 将不会被释放 迟早这会导致OutOfMemoryError 我拍了一张heap dump我意识到它是由
  • VLC录制rtsp流

    我在使用 VLC 播放器录制 rtsp 流时遇到问题 实际上我的方法在 MacOS X 中有效 但在 Windows 中无效 命令行 vlc vvv rtsp admin email protected cdn cgi l email pr
  • 如何使用 Powershell 枚举 IIS 网站并查找每个网站的应用程序池?

    我可以使用以下方式搜索网站 Get WmiObject Namespace root WebAdministration Class Site Authentication PacketPrivacy ComputerName server
  • 使用 CUDA 生成排列

    我正在阅读有关 CUDA 的内容 我尝试实现一个简单的代码来创建每个可能的排列array a b c d 但我不知道如何实现 CUDA 方式 因为我读到的所有示例都是这种形式a blockIdx x b blockIdx x c block
  • Xcode 无法使用 Apple ID 登录

    最近我想用Xcode归档我的App 然后Xcode说我必须再次输入我的Apple ID和密码 也许我的登录信息丢失了 我又尝试了一次 但总是失败 If I input a wrong password I get a correct res
  • 堆管理

    我知道有一个元数据存储在期间使用的辅助信息free realloc 当我们只提供指针时 我对堆几乎没有疑问 堆栈是按进程分配的 毫无疑问 但对堆不确定 无论堆信息是全局维护的 还是每个进程都会有某种机制来保存有关为该特定进程分配的内存的信息
  • Nose 未运行 Django 文档测试

    如同这个问题 https stackoverflow com questions 2614363 why isnt django nose running the doctests in my models 然而 就我而言 我的模型都没有d
  • Facebook 个人资料 ID 和 uid 之间有区别吗?

    我有一个应用程序 我通过以下代码片段获取应用程序用户的 Facebook uid FB login function response if response authResponse FB getLoginStatus function
  • 如何使用 Dagger2 在 ViewModel 中注入依赖项

    我正在尝试在现有项目中实现新的架构 MVVM RxJava2 Dagger2 Retrofit 我已经建立了整个上述架构并在 HomeActivity 上进行了测试 HomeViewModel 中注入的依赖项 所以现在我试图在Fragmen
  • 在 php 中统计 mongodb

    我有一个集合用户 它有2个参数 用户名 付费 用户名是一个字符串 付费也是一个字符串 我应该计算有多少个带有参数paid true 的用户 这是我的尝试 如果有 mongo 专家可以帮忙 谢谢 新驱动没有实现 cursor gt count
  • 仅使用 CSS 突出显示表行中的所有行跨度

    有没有办法让 CSS 突出显示整行 包括行跨度内的单元格 而不仅仅是第一行 正如你从示例中看到的 只有该行的第一行被突出显示 而其他单元格没有突出显示 这显然看起来很混乱 我宁愿只使用 CSS 来清除它 但如果有的话会使用 Javascri
  • 将正则表达式与 NSPredicate 结合使用

    Aim Using NSPredicate我想使用正则表达式来匹配所有以 Test 开头的字符串 我特别想用Regex and NSPredicate 问题 我犯了什么错误 使用正则表达式来实现我想要做的事情的正确方法是什么 代码 我的尝试
  • 从 Git 存储库克隆后的 Laravel Sail

    我目前有自己的 Laravel 应用程序在 Docker 上运行 在 Windows 11 上使用 Laravel sail 在 WSL2 上使用 Ubuntu 这工作正常并且符合预期 我已将我的工作推送到 Git 存储库 但如何将其拉到新
  • 添加页面时,refinerycms 不工作

    尝试使用 RefineryCMS 从仪表板添加新页面时出现此错误 SQLite3 ConstraintException refinery page translations refinery page id 不得为 NULL INSERT
  • FtpWebRequest ListDirectory 不返回所有文件

    我正在尝试从包含大约 9000 个文件的 FTP 位置检索文件列表 但下面的代码总是只给出 97 个文件 在第 98 个文件的循环开始时 StreamReader Peek 变为 1 输出 test txt 始终仅包含前 97 个文件 就像
  • 创建没有版本号的 Grails 3 WAR

    在 Grails 2 中 我们可以像这样定义 WAR 文件名 grails project war file target appName war 如何覆盖 Grails 3 中的 war 文件名 谢谢杰夫 我在 build gradle
  • 动态生成的 IL 中的值类型转换

    Update一年多后 我终于意识到了这种行为的原因 本质上 一个对象不能被拆箱为与它不同的类型 被装箱为 即使该类型强制转换或转换为目标 类型 如果你不知道正确的类型 你必须发现它 不知何故 作业可能完全有效 但不可行 让这一切自动发生 例