如何在属性设置器中发出验证代码

发布于 2024-12-19 18:47:55 字数 4020 浏览 0 评论 0原文

在我的 Silverlight 客户端上,我在运行时生成一个类以将其绑定到数据网格。我正在使用基于 this< 的方法/a> 博客文章。 现在我想通过在属性设置器中调用 ValidateProperty 来使用数据网格单元格验证。但由于属性是在运行时生成的,我需要在 Reflection.Emit 中执行此操作。

这是我想在 IL 中生成的 C#:

    public int TestProperty
    {
        get { return testProperty; }
        set 
        {
            Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "TestProperty" });
            testProperty = value; 
        }
    }

这就是 ILspy 在 IL 中反编译此方法的方式:

.property int32 TestProperty
{
    .get public hidebysig specialname 
        instance int32 get_TestProperty () cil managed 
    {
        // Method begins at RVA 0x224c
        // Code size 12 (0xc)
        .maxstack 1
        .locals init (
            [0] int32 
        )

        IL_0000: nop
        IL_0001: ldarg.0
        IL_0002: ldfld int32 class SilverlightApplication2.testclass::testProperty
        IL_0007: stloc.0
        IL_0008: br.s IL_000a
        IL_000a: ldloc.0
        IL_000b: ret
    } // End of method testclass.get_TestProperty
    .set public hidebysig specialname 
        instance void set_TestProperty (
            int32 value
        ) cil managed 
    {
        // Method begins at RVA 0x2264
        // Code size 43 (0x2b)
        .maxstack 5
        .locals init (
            [0] class [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext 
        )

        IL_0000: nop
        IL_0001: ldarg.1
        IL_0002: box int32
        IL_0007: ldarg.0
        IL_0008: ldnull
        IL_0009: ldnull
        IL_000a: newobj instance void [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext::.ctor(object, class [mscorlib]System.IServiceProvider, class [mscorlib]System.Collections.Generic.IDictionary`2<object, object>)
        IL_000f: stloc.0
        IL_0010: ldloc.0
        IL_0011: ldstr "TestProperty"
        IL_0016: callvirt instance void [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext::set_MemberName(string)
        IL_001b: nop
        IL_001c: ldloc.0
        IL_001d: call void [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.Validator::ValidateProperty(object, class [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext)
        IL_0022: nop
        IL_0023: ldarg.0
        IL_0024: ldarg.1
        IL_0025: stfld int32 class SilverlightApplication2.testclass::testProperty
        IL_002a: ret
    } // End of method testclass.set_TestProperty
}

这是我尝试在 Reflection.Emit 中编写它:

        setIL.Emit(OpCodes.Ldarg_1);
        setIL.Emit(OpCodes.Box, typeof(Int32));
        setIL.Emit(OpCodes.Ldarg_0);
        setIL.Emit(OpCodes.Ldnull);
        setIL.Emit(OpCodes.Ldnull);

        Type[] types = new Type[3];
        types[0] = typeof(object);
        types[1] = typeof(IServiceProvider);
        types[2] = typeof(IDictionary<object, object>);

        setIL.Emit(OpCodes.Newobj, typeof(System.ComponentModel.DataAnnotations.ValidationContext).GetConstructor(types));
        setIL.Emit(OpCodes.Stloc_0);
        setIL.Emit(OpCodes.Ldloc_0);
        setIL.Emit(OpCodes.Ldstr, "TestProperty");
        setIL.Emit(OpCodes.Callvirt, typeof(System.ComponentModel.DataAnnotations.ValidationContext).GetMethod("set_MemberName"));
        setIL.Emit(OpCodes.Ldloc_0);
        setIL.Emit(OpCodes.Call, typeof(System.ComponentModel.DataAnnotations.Validator).GetMethod("ValidateProperty"));

        setIL.Emit(OpCodes.Ldarg_0);
        setIL.Emit(OpCodes.Ldarg_1);
        setIL.Emit(OpCodes.Stfld, fieldBuilder);
        setIL.Emit(OpCodes.Ret);

这只是我代码的一部分,我设法让它工作而无需Validator.ValidateProperty 与最后 4 行代码。对于其他 16 行,我想添加验证功能,但现在这会导致“操作可能会破坏运行时的稳定性”异常。

On my Silverlight client, I'm generating a class at run-time to bind it to a datagrid. I'm using a method based on this blog post.
Now I want to use datagrid cellvalidation by calling ValidateProperty in the property setter. But as the properties are generated at runtime, I need to do this in Reflection.Emit.

This is the C# I want to generate in IL:

    public int TestProperty
    {
        get { return testProperty; }
        set 
        {
            Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "TestProperty" });
            testProperty = value; 
        }
    }

This is how ILspy decompiles this method in IL:

.property int32 TestProperty
{
    .get public hidebysig specialname 
        instance int32 get_TestProperty () cil managed 
    {
        // Method begins at RVA 0x224c
        // Code size 12 (0xc)
        .maxstack 1
        .locals init (
            [0] int32 
        )

        IL_0000: nop
        IL_0001: ldarg.0
        IL_0002: ldfld int32 class SilverlightApplication2.testclass::testProperty
        IL_0007: stloc.0
        IL_0008: br.s IL_000a
        IL_000a: ldloc.0
        IL_000b: ret
    } // End of method testclass.get_TestProperty
    .set public hidebysig specialname 
        instance void set_TestProperty (
            int32 value
        ) cil managed 
    {
        // Method begins at RVA 0x2264
        // Code size 43 (0x2b)
        .maxstack 5
        .locals init (
            [0] class [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext 
        )

        IL_0000: nop
        IL_0001: ldarg.1
        IL_0002: box int32
        IL_0007: ldarg.0
        IL_0008: ldnull
        IL_0009: ldnull
        IL_000a: newobj instance void [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext::.ctor(object, class [mscorlib]System.IServiceProvider, class [mscorlib]System.Collections.Generic.IDictionary`2<object, object>)
        IL_000f: stloc.0
        IL_0010: ldloc.0
        IL_0011: ldstr "TestProperty"
        IL_0016: callvirt instance void [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext::set_MemberName(string)
        IL_001b: nop
        IL_001c: ldloc.0
        IL_001d: call void [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.Validator::ValidateProperty(object, class [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext)
        IL_0022: nop
        IL_0023: ldarg.0
        IL_0024: ldarg.1
        IL_0025: stfld int32 class SilverlightApplication2.testclass::testProperty
        IL_002a: ret
    } // End of method testclass.set_TestProperty
}

And this is me trying to write it in Reflection.Emit:

        setIL.Emit(OpCodes.Ldarg_1);
        setIL.Emit(OpCodes.Box, typeof(Int32));
        setIL.Emit(OpCodes.Ldarg_0);
        setIL.Emit(OpCodes.Ldnull);
        setIL.Emit(OpCodes.Ldnull);

        Type[] types = new Type[3];
        types[0] = typeof(object);
        types[1] = typeof(IServiceProvider);
        types[2] = typeof(IDictionary<object, object>);

        setIL.Emit(OpCodes.Newobj, typeof(System.ComponentModel.DataAnnotations.ValidationContext).GetConstructor(types));
        setIL.Emit(OpCodes.Stloc_0);
        setIL.Emit(OpCodes.Ldloc_0);
        setIL.Emit(OpCodes.Ldstr, "TestProperty");
        setIL.Emit(OpCodes.Callvirt, typeof(System.ComponentModel.DataAnnotations.ValidationContext).GetMethod("set_MemberName"));
        setIL.Emit(OpCodes.Ldloc_0);
        setIL.Emit(OpCodes.Call, typeof(System.ComponentModel.DataAnnotations.Validator).GetMethod("ValidateProperty"));

        setIL.Emit(OpCodes.Ldarg_0);
        setIL.Emit(OpCodes.Ldarg_1);
        setIL.Emit(OpCodes.Stfld, fieldBuilder);
        setIL.Emit(OpCodes.Ret);

This is only a part of my code, I managed to get it working without the Validator.ValidateProperty with the last 4 lines of code. With the 16 other lines I want to add the validate functionality, but right now this results in a 'Operation could destabilize the runtime' exception.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

寂寞清仓 2024-12-26 18:47:55

我自己想出来了:)

这是您需要在属性设置器中发出 Validator.Validate 的代码:

        MethodBuilder setPropMthdBldr =
            tb.DefineMethod("set_" + "TestProperty",
              MethodAttributes.Public |
              MethodAttributes.SpecialName |
              MethodAttributes.HideBySig,
              null, new Type[] { propertyType });


        ConstructorInfo ctor1 = typeof(ValidationContext).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                                                                         null,
                                                                         new Type[]{
                                                                                typeof(Object),
                                                                                typeof(IServiceProvider),
                                                                                typeof(IDictionary<object, object>)},
                                                                         null);

        MethodInfo method2 = typeof(ValidationContext).GetMethod("set_MemberName",
                                                                 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                                                                 null,
                                                                 new Type[]{typeof(String)},
                                                                 null);

        MethodInfo method3 = typeof(Validator).GetMethod("ValidateProperty",
                                                         BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
                                                         null,
                                                         new Type[]{
                                                                typeof(Object),
                                                                typeof(ValidationContext)},
                                                         null);

        ILGenerator setIL = setPropMthdBldr.GetILGenerator();

        setIL.DeclareLocal(typeof(System.ComponentModel.DataAnnotations.ValidationContext));

        setIL.Emit(OpCodes.Nop);
        setIL.Emit(OpCodes.Ldarg_1);
        setIL.Emit(OpCodes.Box, typeof(Int32));  //in this case it's int32, should be your property type
        setIL.Emit(OpCodes.Ldarg_0);
        setIL.Emit(OpCodes.Ldnull);
        setIL.Emit(OpCodes.Ldnull);
        setIL.Emit(OpCodes.Newobj, ctor1);
        setIL.Emit(OpCodes.Stloc_0);
        setIL.Emit(OpCodes.Ldloc_0);
        setIL.Emit(OpCodes.Ldstr, "TestProperty");
        setIL.Emit(OpCodes.Callvirt, method2);
        setIL.Emit(OpCodes.Nop);
        setIL.Emit(OpCodes.Ldloc_0);
        setIL.Emit(OpCodes.Call, method3);
        setIL.Emit(OpCodes.Nop);
        setIL.Emit(OpCodes.Ldarg_0);
        setIL.Emit(OpCodes.Ldarg_1);
        setIL.Emit(OpCodes.Stfld, fieldBuilder);  //the fieldbuilder you are using to define the private field
        setIL.Emit(OpCodes.Ret);

Figured it out myself :)

This is the code you need to emit Validator.Validate in your property setter:

        MethodBuilder setPropMthdBldr =
            tb.DefineMethod("set_" + "TestProperty",
              MethodAttributes.Public |
              MethodAttributes.SpecialName |
              MethodAttributes.HideBySig,
              null, new Type[] { propertyType });


        ConstructorInfo ctor1 = typeof(ValidationContext).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                                                                         null,
                                                                         new Type[]{
                                                                                typeof(Object),
                                                                                typeof(IServiceProvider),
                                                                                typeof(IDictionary<object, object>)},
                                                                         null);

        MethodInfo method2 = typeof(ValidationContext).GetMethod("set_MemberName",
                                                                 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                                                                 null,
                                                                 new Type[]{typeof(String)},
                                                                 null);

        MethodInfo method3 = typeof(Validator).GetMethod("ValidateProperty",
                                                         BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
                                                         null,
                                                         new Type[]{
                                                                typeof(Object),
                                                                typeof(ValidationContext)},
                                                         null);

        ILGenerator setIL = setPropMthdBldr.GetILGenerator();

        setIL.DeclareLocal(typeof(System.ComponentModel.DataAnnotations.ValidationContext));

        setIL.Emit(OpCodes.Nop);
        setIL.Emit(OpCodes.Ldarg_1);
        setIL.Emit(OpCodes.Box, typeof(Int32));  //in this case it's int32, should be your property type
        setIL.Emit(OpCodes.Ldarg_0);
        setIL.Emit(OpCodes.Ldnull);
        setIL.Emit(OpCodes.Ldnull);
        setIL.Emit(OpCodes.Newobj, ctor1);
        setIL.Emit(OpCodes.Stloc_0);
        setIL.Emit(OpCodes.Ldloc_0);
        setIL.Emit(OpCodes.Ldstr, "TestProperty");
        setIL.Emit(OpCodes.Callvirt, method2);
        setIL.Emit(OpCodes.Nop);
        setIL.Emit(OpCodes.Ldloc_0);
        setIL.Emit(OpCodes.Call, method3);
        setIL.Emit(OpCodes.Nop);
        setIL.Emit(OpCodes.Ldarg_0);
        setIL.Emit(OpCodes.Ldarg_1);
        setIL.Emit(OpCodes.Stfld, fieldBuilder);  //the fieldbuilder you are using to define the private field
        setIL.Emit(OpCodes.Ret);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文