我正在围绕.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
https://github.com/dotnet/corefx/issues/29254
A PR addressing the issue已经提交并合并,但遗憾的是修复尚未发布.它在.NET Core 2.2.0中的can be expected.
在那之前你不能做很多事情,因为它也在this discussion年底之前说过.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。