Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 创建DynamicMethod以在任何类型上设置只读字段_C#_Cil_Reflection.emit_Dynamicmethod - Fatal编程技术网

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

C# 创建DynamicMethod以在任何类型上设置只读字段,c#,cil,reflection.emit,dynamicmethod,C#,Cil,Reflection.emit,Dynamicmethod,我的目标是在运行时创建一个委托,该委托可以将任何引用类型中的任何字段(包括readonly)设置为用户指定的值。不幸的是,当包含类型的程序集指定[AllowPartiallyTrustedCallers]属性时,我当前的实现在运行时抛出了一个VerificationException AssemblyOne: AssemblyTwo: Main: 使用系统; 运用系统反思; 使用System.Reflection.Emit; 使用AssemblyOne; 使用汇编二; 名称空间主 { 班级

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

AssemblyOne

AssemblyTwo

Main

使用系统;
运用系统反思;
使用System.Reflection.Emit;
使用AssemblyOne;
使用汇编二;
名称空间主
{
班级计划
{
公共类MyType
{
公共MyType(TypeOne TypeOne字段)
{
this.TypeOneField=TypeOneField;
}
公共TypeOne TypeOne字段{get;}
}
静态void Main(字符串[]参数)
{
var fieldInfo=typeof(TypeOne)
.GetTypeInfo()
格特菲尔德先生(
“k__BackingField”,
BindingFlags.Instance | BindingFlags.NonPublic|
(公众),;
变量设置器=(操作)GetReferenceSetter(字段信息);
var myType=新的myType(新的TypeOne(新的TypeTwo(1));
//抛出验证异常
setter(myType.TypeOneField,新的TypeTwo(2));
}
公共静态委托GetReferenceSetter(字段信息字段)
{
var delegateType=typeof(操作)
.MakeGenericType(field.DeclaringType,field.FieldType);
var方法=新的DynamicMethod(
field.Name+“Set”,
无效的
新[]{field.DeclaringType,field.FieldType},
field.DeclaringType,
技能可视性:正确);
var emitter=method.GetILGenerator();
发射器.Emit(操作码.Ldarg_0);
发射器.Emit(操作码.Ldarg_1);
发射器.Emit(操作码.Stfld,字段);
发射器.Emit(操作码.Ret);
返回方法.CreateDelegate(delegateType);
}
}
}
所以
MyType
有一个
TypeOne
,它有一个只读
TypeTwo
。在这个场景中,
DynamicMethod
在运行时抛出一个
VerificationException

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


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

我有一些出口代码可以帮助您

/// <summary>
    /// create a getter delegate for a static field.
    /// </summary>
    /// <typeparam name="TField"></typeparam>
    /// <param name="staticType">the type that contains the field. </param>
    /// <param name="fieldName">the field that you want to get the value.</param>
    /// <returns>the getter delegate.</returns>
    public static Func<TField> CreateStaticFieldGetter<TField>(Type staticType, string fieldName)
    {
        var fieldInfo = staticType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
        var body = Expression.Field(null, fieldInfo);
        Expression<Func<TField>> lambda = Expression.Lambda<Func<TField>>(body);
        return lambda.Compile();
    }


    public static Action<TField> CreateStaticFieldSetter<TField>(Type staticType, string fieldName)
    {
        ParameterExpression p1 = Expression.Parameter(typeof(TField), "p1");
        var fieldInfo = staticType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
        var leftExpr = Expression.Field(null, fieldInfo);
        var body = Expression.Assign(leftExpr, p1);
        Expression<Action<TField>> lambda = Expression.Lambda<Action<TField>>(body, p1);
        return lambda.Compile();
    }



    public static Func<TTarget, TField> CreateInstanceFieldGetter<TTarget, TField>(string fieldName)
    {
        ParameterExpression p1 = Expression.Parameter(typeof(TTarget), "p1");
        var fieldInfo = typeof(TTarget).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
        var body = Expression.Field(Expression.Convert(p1, typeof(TTarget)), fieldInfo);
        Expression<Func<TTarget, TField>> lambda = Expression.Lambda<Func<TTarget, TField>>(body, p1);
        return lambda.Compile();
    }



    public static Action<TTarget, TProperty> CreateInstanceFieldSetter<TTarget, TProperty>(string filedName)
    {
        ParameterExpression p1 = Expression.Parameter(typeof(TTarget), "p1");
        ParameterExpression p2 = Expression.Parameter(typeof(TProperty), "p2");

        var member = typeof(TTarget).GetField(filedName, BindingFlags.NonPublic | BindingFlags.Instance);
        var m1 = Expression.MakeMemberAccess(Expression.Convert(p1, typeof(TTarget)), member);
        BinaryExpression body = Expression.Assign(m1, p2);

        Expression<Action<TTarget, TProperty>> lambda = Expression.Lambda<Action<TTarget, TProperty>>(body, p1, p2);
        return lambda.Compile();
    }
//
///为静态字段创建getter委托。
/// 
/// 
///包含该字段的类型。
///要获取值的字段。
///getter代表。
公共静态函数CreateStaticFieldGetter(类型staticType,字符串fieldName)
{
var fieldInfo=staticType.GetField(fieldName,BindingFlags.NonPublic | BindingFlags.Static);
var body=Expression.Field(null,fieldInfo);
表达式lambda=表达式.lambda(body);
返回lambda.Compile();
}
公共静态操作CreateStaticFieldSetter(类型staticType,字符串fieldName)
{
ParameterExpression p1=表达式参数(typeof(TField),“p1”);
var fieldInfo=staticType.GetField(fieldName,BindingFlags.NonPublic | BindingFlags.Static);
var leftExpr=Expression.Field(null,fieldInfo);
var body=Expression.Assign(leftExpr,p1);
表达式lambda=表达式.lambda(body,p1);
返回lambda.Compile();
}
公共静态函数CreateInstanceFieldGetter(字符串字段名)
{
ParameterExpression p1=表达式参数(typeof(TTarget),“p1”);
var fieldInfo=typeof(TTarget).GetField(fieldName,BindingFlags.NonPublic | BindingFlags.Instance);
var body=Expression.Field(Expression.Convert(p1,typeof(TTarget)),fieldInfo);
表达式lambda=表达式.lambda(body,p1);
返回lambda.Compile();
}
公共静态操作CreateInstanceFieldSetter(字符串文件名)
{
ParameterExpression p1=表达式参数(typeof(TTarget),“p1”);
ParameterExpression p2=表达式参数(typeof(TProperty),“p2”);
var member=typeof(TTarget).GetField(filedName,BindingFlags.NonPublic | BindingFlags.Instance);
var m1=Expression.MakeMemberAccess(Expression.Convert(p1,typeof(TTarget)),成员);
BinaryExpression body=Expression.Assign(m1,p2);
表达式lambda=表达式.lambda(主体,p1,p2);
返回lambda.Compile();
}

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

相反,可以实现这一点的方法是使用其他访问字段的方法,即TypedReference类型

您可以用自己的方法替换库方法,它们仅用于构建适当的委托类型(由于指针原因,无法使用操作)。由于内部RuntimeType,无法直接创建委托类型。令人惊讶的是,DynamicVoke在委托上工作,并且创建了类型引用

但是,要使用这个巫毒,我必须降低组件中的安全检查,因此我不确定它是否也适用于您的组件系统:

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

另外请注意,此代码使用了大量未记录的特性,并且可能随时停止工作。仅在您自己承担风险的情况下使用。

我还建议删除非英语文档和空文档注释。@Aimee Borada,抱歉,当字段为只读时,代码无法工作。确认
stfld
ldflda
在这种情况下都不起作用。你是否愿意接受更“黑客”的方法?顺便说一下,
readonlyusing 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);
        }
    }
}
/// <summary>
    /// create a getter delegate for a static field.
    /// </summary>
    /// <typeparam name="TField"></typeparam>
    /// <param name="staticType">the type that contains the field. </param>
    /// <param name="fieldName">the field that you want to get the value.</param>
    /// <returns>the getter delegate.</returns>
    public static Func<TField> CreateStaticFieldGetter<TField>(Type staticType, string fieldName)
    {
        var fieldInfo = staticType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
        var body = Expression.Field(null, fieldInfo);
        Expression<Func<TField>> lambda = Expression.Lambda<Func<TField>>(body);
        return lambda.Compile();
    }


    public static Action<TField> CreateStaticFieldSetter<TField>(Type staticType, string fieldName)
    {
        ParameterExpression p1 = Expression.Parameter(typeof(TField), "p1");
        var fieldInfo = staticType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
        var leftExpr = Expression.Field(null, fieldInfo);
        var body = Expression.Assign(leftExpr, p1);
        Expression<Action<TField>> lambda = Expression.Lambda<Action<TField>>(body, p1);
        return lambda.Compile();
    }



    public static Func<TTarget, TField> CreateInstanceFieldGetter<TTarget, TField>(string fieldName)
    {
        ParameterExpression p1 = Expression.Parameter(typeof(TTarget), "p1");
        var fieldInfo = typeof(TTarget).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
        var body = Expression.Field(Expression.Convert(p1, typeof(TTarget)), fieldInfo);
        Expression<Func<TTarget, TField>> lambda = Expression.Lambda<Func<TTarget, TField>>(body, p1);
        return lambda.Compile();
    }



    public static Action<TTarget, TProperty> CreateInstanceFieldSetter<TTarget, TProperty>(string filedName)
    {
        ParameterExpression p1 = Expression.Parameter(typeof(TTarget), "p1");
        ParameterExpression p2 = Expression.Parameter(typeof(TProperty), "p2");

        var member = typeof(TTarget).GetField(filedName, BindingFlags.NonPublic | BindingFlags.Instance);
        var m1 = Expression.MakeMemberAccess(Expression.Convert(p1, typeof(TTarget)), member);
        BinaryExpression body = Expression.Assign(m1, p2);

        Expression<Action<TTarget, TProperty>> lambda = Expression.Lambda<Action<TTarget, TProperty>>(body, p1, p2);
        return lambda.Compile();
    }
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;
}
private unsafe static extern void InternalMakeTypedReference(void* result, object target, IntPtr[] flds, RuntimeType lastFieldType);
[assembly: System.Security.SecurityRules(System.Security.SecurityRuleSet.Level1, SkipVerificationInFullTrust=true)]