作为一般规则,托管代码不应导致访问冲突。这发生在本机代码或
unsafe
代码中。但不知何故,通过一些看起来应该太简单而不会出错的 Reflection.Emit 代码,我遇到了访问冲突。
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
namespace Repro
{
internal class Program
{
static void Main()
{
TypeBuilder typeBuilder = BuildType();
var realType = typeBuilder.CreateType();
var arr = Array.CreateInstance(realType, 10);
var caster = typeof(Enumerable).GetMethod("Cast")!.MakeGenericMethod(realType);
var typedArr = caster.Invoke(null, [arr]);
var sorter = realType.GetMethod("Sort")!;
var sorted = sorter.Invoke(null, [typedArr])!;
var bomb = ((IEnumerable)sorted).Cast<object>().ToArray(); //blows up here
}
private static TypeBuilder BuildType()
{
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new("MyAssembly"), AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyAssembly");
var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public, typeof(ValueType));
var field = typeBuilder.DefineField("field", typeof(int), FieldAttributes.Public);
var extractor = typeBuilder.DefineMethod("GetField", MethodAttributes.Private | MethodAttributes.Static, typeof(int), [typeof(int)]);
var il = extractor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, field);
il.Emit(OpCodes.Ret);
var enumType = typeof(IEnumerable<>).MakeGenericType(typeBuilder);
var impl = typeBuilder.DefineMethod("Sort", MethodAttributes.Public | MethodAttributes.Static, enumType, [enumType]);
il = impl.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldtoken, extractor);
var getMethod = typeof(MethodBase).GetMethod("GetMethodFromHandle", [typeof(RuntimeMethodHandle)])!;
il.Emit(OpCodes.Call, getMethod);
var orderFunc = typeof(Enumerable).GetMethods().Single(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
il.Emit(OpCodes.Call, orderFunc.MakeGenericMethod(typeBuilder, typeof(int))); // call Enumerable.OrderBy(enumerable, extractor)
il.Emit(OpCodes.Ret);
return typeBuilder;
}
}
}
去除所有反射,这应该生成大致相当于以下内容的代码:
using System.Linq;
struct MyType
{
public int field;
private static int GetField(MyType value) => value.field;
public static IEnumerable<MyType> Sort(IEnumerable<MyType> values)
{
return values.OrderBy(MyType.GetField);
}
}
//in Main
var arr = new MyType[10];
var sorted = MyType.Sort(arr);
var bomb = sorted.ToArray();
但不知怎的,在
bomb
线上,它抛出了一个AccessViolationException
。我很想了解原因以及如何解决它。
Enumerable.OrderBy()
不接受方法句柄作为参数,我认为您错过了 C# 如何隐式为您创建 Func<T,int>
。
也许你应该尝试生成类似的代码;
struct MyType
{
public int field;
private static int GetField(MyType value) => value.field;
private static Func<MyType, int> func;
public static IEnumerable<MyType> Sort(IEnumerable<MyType> values)
{
return values.OrderBy(func ?? (func = new Func<MyType,int>(GetField)));
}
}
根据 sharplab.io,它应该类似于以下 IL;
ldarg.0
ldsfld class [System.Runtime]System.Func`2<valuetype MyType, int32> MyType::func
dup
brtrue.s IL_001c
pop
ldnull
ldftn int32 MyType::GetField(valuetype MyType)
newobj instance void class [System.Runtime]System.Func`2<valuetype MyType, int32>::.ctor(object, native int)
dup
stsfld class [System.Runtime]System.Func`2<valuetype MyType, int32> MyType::func
call class [System.Linq]System.Linq.IOrderedEnumerable`1<!!0> [System.Linq]System.Linq.Enumerable::OrderBy<valuetype MyType, int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>, class [System.Runtime]System.Func`2<!!0, !!1>)
ret