【.NET】利用 IL 魔法实现随心随意的泛型约束

news/发布时间2024/5/11 20:14:49

众所周知,C# 只支持对 基类/接口/class/struct/new() 以及一些 IDE 魔法的约束,比如这样

public static string Test<T>(T value) where T : ITest
{return value.Test();
}public interface ITest
{string Test();
}

但是如果我们想要随心所欲的约束就不行了

public static string Test<T>(T value) where T : { string Test(); }
{return value.Test();
}

最近无聊乱折腾 MSIL,弄出来好多不能跑的魔法,虽然不能跑但是反编译出的 C# 看着很神奇,其中正好就有想看看能不能弄个神奇的泛型出来,于是我胡写了一段代码

.assembly _
{
}.class public Test
{.method public void .ctor(){ldarg.0call instance void object::.ctor()ret}.method public static void Main(){.entrypointnewobj instance void Test::.ctor()call string Test::Test<class Test>(!!0)call void [mscorlib]System.Console::WriteLine(string)ret}.method public static string Test<T>(!!T t){ldarg.s tcallvirt instance string !!T::Test()ret}.method public string Test(){ldstr "Call instance string Test::Test()"ret}
}

反编译出来是这样的

public class Test
{public static void Main(){Console.WriteLine(Test(new Test()));}public unsafe static string Test<T>(T t){return ((T*)t)->Test();}public string Test(){return "Call instance string Test::Test()";}
}

这段代码是无法运行的,在 .NET Framework 会直接无返回,而在 Mono 会报错

[ERROR] FATAL UNHANDLED EXCEPTION: System.MissingMethodException: Method not found: string .T_REF.Test()
    at Test.Main () [0x00005] in <ddf64a5d94ef4722be4197eb692d9478>:0

于是我就当这是 .NET 泛型的局限性了,后来有群友提醒我说约束会影响运行时,于是我就尝试加上约束

.method public static string Test<(Test) T>(!!T t)
{ldarg.s tcallvirt instance string !!T::Test()ret
}

发现真的能跑了(Framework 依然无返回。。。),于是我就看看能不能同时约束两个类型

.method public static string Test<(Test, Test2) T>(!!T t)
{ldarg.s tcallvirt instance string !!T::Test()ret
}

Mono 成功输出

Call instance string Test::Test()
Call instance string Test2::Test()

而 Framework 直接运行时约束了。。。

未经处理的异常: System.Security.VerificationException: 方法 Test.Test: 类型参数“Test”与类型参数“T”的约束冲突。
    在 Test.Main()

很明显 Mono 给泛型开了洞

随后测试发现,只要约束的类有相关成员就可以正常调用,于是我就利用抽象类做接口

.assembly _
{
}.class public Test
{.field string Test.method public void .ctor(){ldarg.0ldstr "This is Test"stfld string Test::Testldarg.0call instance void object::.ctor()ret}.method public static void Main(){.entrypoint.locals init (class Test test,class Test2 test2)newobj instance void Test::.ctor()stloc.s testldloc.s testcall void Test::Test<class Test>(!!0)ldloc.s testcall string Test::Test<class Test>(!!0)call void [mscorlib]System.Console::WriteLine(string)newobj instance void Test2::.ctor()stloc.s test2ldloc.s test2call void Test::Test<class Test2>(!!0)ldloc.s test2call string Test::Test<class Test2>(!!0)call void [mscorlib]System.Console::WriteLine(string)ret}.method public static void Test<(WithTest) T>(!!T t){ldarg.s tldfld string !!T::Testcall void [mscorlib]System.Console::WriteLine(string)ret}.method public static string Test<(WithTest) T>(!!T t){ldarg.s tcallvirt instance string !!T::Test()ret}.method public string Test(){ldstr "Call instance string Test::Test()"ret}
}.class public Test2
{.field string Test.method public void .ctor(){ldarg.0ldstr "This is Test2"stfld string Test2::Testldarg.0call instance void object::.ctor()ret}.method public newslot virtual string Test(){ldstr "Call instance string Test2::Test()"ret}
}.class public abstract WithTest
{.field string Test.method public newslot abstract virtual string Test(){}
}

正确输出

This is Test
Call instance string Test::Test()
This is Test2
Call instance string Test2::Test()

Framework 自然还是炸的

最后反编译出来长这样

public class Test
{private string m_Test = "This is Test";public static void Main(){Test t = new Test();global::Test.Test<Test>(t);Console.WriteLine(global::Test.Test<Test>(t));Test2 t2 = new Test2();global::Test.Test<Test2>(t2);Console.WriteLine(global::Test.Test<Test2>(t2));}public unsafe static void Test<T>(T t) where T : WithTest{Console.WriteLine(((T*)t)->Test);}public unsafe static string Test<T>(T t) where T : WithTest{return ((T*)t)->Test();}public string Test(){return "Call instance string Test::Test()";}
}public class Test2
{private string m_Test = "This is Test2";public virtual string Test(){return "Call instance string Test2::Test()";}
}public abstract class WithTest
{private string m_Test;public abstract string Test();
}

当然,这种操作仅限娱乐,经测试 .NET Framework 和 .NET Core App 都会卡在约束,所以 .NET 是别想有随意的约束了,不过 C# 题案 "Roles and extensions" 倒是给出了曲线实现方案

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ulsteruni.cn/article/63311747.html

如若内容造成侵权/违法违规/事实不符,请联系编程大学网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

Python-数字取证秘籍(三)

Python 数字取证秘籍(三)原文:zh.annas-archive.org/md5/941c711b36df2129e5f7d215d3712f03 译者:飞龙 协议:CC BY-NC-SA 4.0第六章:阅读电子邮件和获取名称的配方 本章涵盖了以下配方:解析 EML 文件查看 MSG 文件订购外卖盒子里有什么?解析 PST 和 OST 邮箱介绍 一旦计…

一个.NET内置依赖注入的小型强化版

前言 .NET生态中有许多依赖注入容器。在大多数情况下,微软提供的内置容器在易用性和性能方面都非常优秀。外加ASP.NET Core默认使用内置容器,使用很方便。 但是笔者在使用中一直有一个头疼的问题:服务工厂无法提供请求的服务类型相关的信息。这在一般情况下并没有影响,但是…

宝塔面板mysql无法启动问题如何解决

宝塔面板无法启动的问题和解决 如果你的宝塔里面的mysql无法启动了,请先看是不是以下的配置问题 1.是不是你的3306端口被占用了导致mysql无法启动 2.是不是磁盘空间不足导致的无法启动 如果都不是这些问题再继续向下看 常见问题: 1、Mysql安装好后或迁移文件后无法启动 2、My…

小程序上显示富文本

功能:富文本内容格式化、拿到富文本里的所有图片点击放大图片util.tsexport function formatRichText(html: any) { // 富文本内容格式化let arrText = html;//正则匹配不含style="" 或 style= 的img标签var regex1 = new RegExp("(i?)(\<img)(?!(.*?sty…

TPU-MLIR实现技术详细介绍

TPU-MLIR实现技术详细介绍 TPU-MLIR简介 后文假定用户已经处于docker里面的/workspace目录。 编译ONNX模型 以 yolov5s.onnx 为例, 介绍如何编译迁移一个onnx模型至BM1684X TPU平台运行。 该模型来自yolov5的官网: https://github.com/ultralytics/yolov5/releases/download/v6…

PostgreSQL源码学习 win10源码编译安装

源码学习的第一步是源码安装,只有用源码安装才能之后在此基础上阅读、调试、开发系统。 我这里安装的是PostgreSQL 13.2版本,其他版本大同小异,如有出入,以最新版本的文档为准:PostgreSQL最新版文档 一、下载源码 源码下载链接,内含各自版本的源码:PG源码 比如13.2版本的…