加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-15 22:43:49 所属栏目:百科 来源:网络整理
导读:我的目标是在运行时创建一个委托,可以将任何引用类型中的任何字段(包括readonly)设置为用户指定的值.不幸的是,当包含类型的程序集指定[AllowPartiallyTrustedCallers]属性时,我的当前实现会在运行时抛出VerificationException. AssemblyOne: [assembly: All
我的目标是在运行时创建一个委托,可以将任何引用类型中的任何字段(包括readonly)设置为用户指定的值.不幸的是,当包含类型的程序集指定[AllowPartiallyTrustedCallers]属性时,我的当前实现会在运行时抛出VerificationException.

AssemblyOne:

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

    public TypeTwo TypeTwoField { get; }
}

AssemblyTwo:

[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.

是否有可能创建一个适用于您抛出的任何声明类型字段类型的委托?如果是这样,怎么样?

我意识到在构造之后永远不会设置只读字段,但是这样做的目的是用于反序列化&深拷贝.

解决方法

动态方法在修改其安全性方面非常有限.我怀疑使用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;
}

FromMethodInfo和NewCustomDelegateType来自SharpUtils,需要调用InternalMakeTypedReference,它能够获得对任何字段的引用.它的签名如下:

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

您可以用自己的库替换库方法,只需要构建适当的委托类型(由于指针而无法使用Action).由于内部RuntimeType,您无法直接创建委托类型.令人惊讶的是,DynamicInvoke在委托上工作,并创建了类型引用.

但是,要使用这个伏都教,我不得不降低组件中的安全检查,所以我不确定它是否也适用于你的装配系统:

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

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读