This question is very similar to a two other questions see: first, second. However those are pretty out dated to say the least and I hope things have changed with .Net 5.
Now first up let me clarify the issue. With a simple example that tries to get the underlying array of a List<int>.
var method = new DynamicMethod("GetUnderlyingArray", typeof(int[]), new[] { typeof(List<int>) }, typeof(List<int>), true);
var ilGenerator = method.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, typeof(List<int>).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance));
ilGenerator.Emit(OpCodes.Ret);
var arrayGetter = (Func<List<int>, int[]>)method.CreateDelegate(typeof(Func<List<int>, int[]>));
This works perfectly fine as I am able to tell the DynamicMethod
to skip the visibility checks (even though it works as well, when the last parameter of the DynamicMethod
constructor true
is removed).
However, when I am trying to do the same with the example down below, it will throw a FieldAccessException
.
var assemblyName = new AssemblyName("Example");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var dynamicModule = assemblyBuilder.DefineDynamicModule(assemblyName.Name + ".dll");
var type = dynamicModule.DefineType("GetUnderlyingArrayClass");
var method = type.DefineMethod("GetUnderlyingArray", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, typeof(int[]), new[] { typeof(List<int>) });
var ilGenerator = method.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, typeof(List<int>).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance));
ilGenerator.Emit(OpCodes.Ret);
type.CreateType();
var arrayGetter = (Func<List<int>, int[]>)type.GetMethod("GetUnderlyingArray").CreateDelegate(typeof(Func<List<int>, int[]>));
System.FieldAccessException: Attempt by method 'GetUnderlyingArrayClass.GetUnderlyingArray(System.Collections.Generic.List'1)' to access field 'System.Collections.Generic.List'1<System.Int32>._items' failed. + GetUnderlyingArrayClass.GetUnderlyingArray(List)
Here is a .Net fiddle link with the code shown above.
Now, one of the questions I mentioned points to the following attribute ReflectionPermissionAttribute
. However as it states on the documentation Code Access Security is not supported or honored by the runtime.
. From what I understand, this basically means that .Net Core/.Net 5 do not support CAS.
Here is where I am getting confused. Setting the skipVisibility
parameter to true
or false
doesn't actually matter. I'd assume that is due to the fact that I am running the code in a .Net 5 environment. However, if CAS is not supported on .Net 5, why am I still able to read out the private field?
The goal is obviously to access a private field/method from a dynamically generated method using the DefineType
/DefineMethod
API's.
I am still not able to explain why setting the
skipVisibility
parameter totrue
/false
doesn't matter, however PathogenDavid over on the GitHub from the csharp runtime discussion repository showed me a solution which was way easier than expected.See this .Net Fiddle for a working example.
As he said the
IgnoresAccessChecksTo
attribute is used to bypass the checks.