gpt4 book ai didi

c# - 创建 DynamicMethod 以在任何类型上设置只读字段

转载 作者:行者123 更新时间:2023-11-30 12:39:53 24 4
gpt4 key购买 nike

我的目标是在运行时创建一个委托(delegate),它可以将任何引用类型中的任何字段(包括 readonly)设置为用户指定的值。不幸的是,当包含类型的程序集指定 [AllowPartiallyTrustedCallers] 属性时,我当前的实现会在运行时抛出 VerificationException

AssemblyOne:

[assembly: AllowPartiallyTrustedCallers]
public class TypeOne
{
public TypeOne(TypeTwo typeTwoField)
{
this.TypeTwoField = typeTwoField;
}

public TypeTwo TypeTwoField { get; }
}

程序集二:

[assembly: AllowPartiallyTrustedCallers]
public class TypeTwo
{
public TypeTwo(int i)
{
this.Int = i;
}

public int Int { get; }
}

主要:

using System;
using System.Reflection;
using System.Reflection.Emit;
using AssemblyOne;
using AssemblyTwo;

namespace Main
{
class Program
{
public class MyType
{
public MyType(TypeOne typeOneField)
{
this.TypeOneField = typeOneField;
}

public TypeOne TypeOneField { get; }
}

static void Main(string[] args)
{
var fieldInfo = typeof(TypeOne)
.GetTypeInfo()
.GetField(
"<TypeTwoField>k__BackingField",
BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Public);
var setter = (Action<TypeOne, TypeTwo>) GetReferenceSetter(fieldInfo);
var myType = new MyType(new TypeOne(new TypeTwo(1)));

// Throws VerificationException
setter(myType.TypeOneField, new TypeTwo(2));
}

public static Delegate GetReferenceSetter(FieldInfo field)
{
var delegateType = typeof(Action<,>)
.MakeGenericType(field.DeclaringType, field.FieldType);

var method = new DynamicMethod(
field.Name + "Set",
null,
new[] {field.DeclaringType, field.FieldType},
field.DeclaringType,
skipVisibility: true);

var emitter = method.GetILGenerator();
emitter.Emit(OpCodes.Ldarg_0);
emitter.Emit(OpCodes.Ldarg_1);
emitter.Emit(OpCodes.Stfld, field);
emitter.Emit(OpCodes.Ret);

return method.CreateDelegate(delegateType);
}
}
}

所以 MyType 有一个 TypeOne,它有一个只读的 TypeTwo。在这种情况下,DynamicMethod 在运行时抛出一个 VerificationException

是否可以创建这样一个适用于任何声明类型 + 字段类型的委托(delegate)?如果是,怎么办?

我意识到 readonly 字段不应该在构造后设置,但这样做的目的是为了反序列化和深度复制。

最佳答案

动态方法在修改其安全性方面非常有限。我怀疑使用 AssemblyBuilder 可能会绕过安全检查,但我还没有尝试过。

相反,可能可以使用其他方法访问TypedReference 类型的字段。

public static unsafe void SetValue<T>(object inst, FieldInfo fi, T val)
{
var mi = typeof(TypedReference).GetMethod("InternalMakeTypedReference", BindingFlags.NonPublic | BindingFlags.Static);
var sig = MethodSignature.FromMethodInfo(mi);
var del = ReflectionTools.NewCustomDelegateType(sig.ReturnType, sig.ParameterTypes);
var inv = mi.CreateDelegate(del);
TypedReference tr;
var ptr = Pointer.Box(&tr, typeof(void*));
inv.DynamicInvoke(ptr, inst, new[]{fi.FieldHandle.Value}, fi.FieldType);
__refvalue(tr, T) = val;
}

FromMethodInfoNewCustomDelegateType 来自 SharpUtils 并且需要调用 InternalMakeTypedReference,它能够获取对任何字段的引用。它的签名看起来像这样:

private unsafe static extern void InternalMakeTypedReference(void* result, object target, IntPtr[] flds, RuntimeType lastFieldType);

您可以用自己的方法替换库方法,只需要它们来构建适当的委托(delegate)类型(Action 由于指针的原因无法使用)。由于内部 RuntimeType,您不能直接创建委托(delegate)类型。令人惊讶的是,DynamicInvoke 在委托(delegate)上运行,并创建了类型引用。

但是,为了使用这个巫术,我不得不稍微降低程序集中的安全检查,所以我不确定它是否也适用于您的程序集系统:

[assembly: System.Security.SecurityRules(System.Security.SecurityRuleSet.Level1, SkipVerificationInFullTrust=true)]

另请注意,此代码使用了大量未记录的功能,并且可能随时停止工作。使用风险自负。

关于c# - 创建 DynamicMethod 以在任何类型上设置只读字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43646430/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com