c# – 生成正确的IL以使用带有通用类的新“in”修饰符的参数调用
发布时间:2020-12-15 22:53:11 所属栏目:百科 来源:网络整理
导读:我正在围绕.NET Core 2.1中新的System.IO.Pipelines包编写一个 serialization/deserialization framework.在生成IL以使用带有通用类的新“in”修饰符的参数调用虚方法时,我遇到了一个问题.这基本上是我试图调用的方法签名: public virtual T DoSomething(in
|
我正在围绕.NET Core 2.1中新的System.IO.Pipelines包编写一个
serialization/deserialization framework.在生成IL以使用带有通用类的新“in”修饰符的参数调用虚方法时,我遇到了一个问题.这基本上是我试图调用的方法签名:
public virtual T DoSomething(in ReadOnlySpan<byte> memory,T o); 如果我取消虚拟修改器,我运行的代码运行正常.一旦我添加虚拟修饰符,我在尝试调用生成的代码时会得到一个MethodNotFound异常.我也注意到如果我不在方法参数的任何地方使用in修饰符,它仍然可以正常工作.如果我从类中取消泛型参数(并保留in参数),则调用也适用于虚拟修饰符.它只在使用in修饰符时崩溃并且似乎使用泛型类型. 我已经将我的代码缩减为一个最小的例子,你可以在下面看到(对于代码转储很抱歉,代码中有很多我认为与整个问题相关的内容). using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace MessageStream.Bug
{
public class BugReproduction
{
public static void Main(string[] args)
{
var test = new TestClass<int>();
var span = new ReadOnlySpan<byte>(new byte[] { 1 });
test.OuterDoSomething(span,10);
}
}
public class TestClass<T> where T : new()
{
private ITestInterface<T> testInterfaceImpl;
public TestClass()
{
Initialize();
}
public T OuterDoSomething(in ReadOnlySpan<byte> memory,T o)
{
return testInterfaceImpl.DoSomething(in memory,o);
}
// Generates a class that implements the ITestInterface<T>.DoSomething
// The generated class basically just calls testClass.DoSomething(in memory,o);
private void Initialize()
{
Type concreteType = GetType();
Type interfaceType = typeof(ITestInterface<T>);
var methodToOverride = interfaceType.GetMethod(nameof(ITestInterface<T>.DoSomething));
string overrideMethodName = string.Format("{0}.{1}",interfaceType.FullName,methodToOverride.Name);
var typeBuilder = CreateTypeBuilderForDeserializer(GetType().Name);
var thisField = typeBuilder.DefineField("testClass",concreteType,FieldAttributes.Private);
var constructor = typeBuilder.DefineConstructor(
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,CallingConventions.HasThis,new[] { concreteType });
var constructorIlGenerator = constructor.GetILGenerator();
constructorIlGenerator.Emit(OpCodes.Ldarg_0);
constructorIlGenerator.Emit(OpCodes.Ldarg_1);
constructorIlGenerator.Emit(OpCodes.Stfld,thisField);
constructorIlGenerator.Emit(OpCodes.Ret);
var doSomethingMethodBuilder = typeBuilder.DefineMethod(
overrideMethodName,MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.Virtual | MethodAttributes.Final,typeof(T),new Type[0],new[]
{
typeof(ReadOnlySpan<byte>).MakeByRefType(),typeof(T)
},new[]
{
new [] { typeof(InAttribute) },new Type[0]
},new[]
{
new Type[0],new Type[0]
});
doSomethingMethodBuilder.DefineParameter(1,ParameterAttributes.In,"memory")
// I pulled this from a decompiled assembly. You will get a signature doesnt match exception if you don't include it.
.SetCustomAttribute(typeof(IsReadOnlyAttribute).GetConstructors()[0],new byte[] { 01,00,00 });
doSomethingMethodBuilder.DefineParameter(2,ParameterAttributes.None,"o");
// Build method body
var methodIlGenerator = doSomethingMethodBuilder.GetILGenerator();
// Emit the call to the "DoSomething" method.
// This fails if the virtual keyword is used on the method.
methodIlGenerator.Emit(OpCodes.Ldarg_0);
methodIlGenerator.Emit(OpCodes.Ldfld,thisField);
methodIlGenerator.Emit(OpCodes.Ldarg_1);
methodIlGenerator.Emit(OpCodes.Ldarg_2);
methodIlGenerator.Emit(OpCodes.Callvirt,concreteType.GetMethod("DoSomething"));
methodIlGenerator.Emit(OpCodes.Ret);
// Point the interfaces method to the overidden one.
typeBuilder.DefineMethodOverride(doSomethingMethodBuilder,methodToOverride);
// Create type and create an instance
Type objectType = typeBuilder.CreateType();
testInterfaceImpl = (ITestInterface<T>)Activator.CreateInstance(objectType,this);
}
/// <summary>
/// This will throw a MethodNotFound exception. If you remove virtual it will work though.
/// </summary>
public virtual T DoSomething(in ReadOnlySpan<byte> memory,T o)
{
Console.WriteLine(memory[0]);
Console.WriteLine(o);
return new T();
}
private static TypeBuilder CreateTypeBuilderForDeserializer(string name)
{
var typeSignature = $"{name}{Guid.NewGuid().ToString().Replace("-","")}";
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an,AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule($"{name}{Guid.NewGuid().ToString()}Module");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,null,new[] { typeof(ITestInterface<T>) });
return tb;
}
}
public interface ITestInterface<T>
{
T DoSomething(in ReadOnlySpan<byte> memory,T o);
}
}
有任何想法吗?我一直在撞墙,试图解决这个问题几个星期了.您可以在my repository中找到实际的真实世界代码.检查benchmark project以了解正在发生的事情/如何使用它. 解决方法
这是CoreCLR中的已知错误:
https://github.com/dotnet/corefx/issues/29254 A PR addressing the issue已经提交并合并,但遗憾的是修复尚未发布.它在.NET Core 2.2.0中的can be expected. 在那之前你不能做很多事情,因为它也在this discussion年底之前说过. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
