zoukankan      html  css  js  c++  java
  • [原创]Silverlight中动态数据验证和动态自动计算的Reflection反射实现

     

    Silverlight中动态数据验证和动态自动计算的Reflection反射实现

     

    上周在博客园发了个首页随笔,因为被误认为是广告而被移出首页,这次发首页,特地备足了技术材料,结合FreeForm实际的开发情况,从技术上分析在Silverlight中实现动态数据验证和自动计算的方法。我们知道在.Net 4.0标准类库中,反射的类很全,非常好用,但在Silverlight类库中涉及反射的命名空间虽然也有System.Reflection System.Reflection.Emit ,但类和方法大大的缩减了。以前我们在WinForm或者ASP.Net中实现动态验证和动态计算非常容易,可以通过类似如下代码实现:

     

           

    View Code
    // compile string code to create class, generate an intance and mothod

    public Expression(string expression, string fieldID, AppCom appcom)

    {

    this.Com = appcom;

    if (expression.IndexOf("return") <0) expression ="return "+ expression +";";

    string className ="ExpressionValidate";

    string methodName ="Compute";

    if (!string.IsNullOrEmpty(fieldID))

    {

    methodName
    = ("Validate_"+ fieldID).Replace(":", "_");

    methodName
    = methodName.Replace("@", "Att_");

    }

    CompilerParameters cp
    =new CompilerParameters();

    cp.GenerateInMemory
    =true;

    cp.ReferencedAssemblies.Add(
    "System.dll");

    cp.ReferencedAssemblies.Add(
    "mscorlib.dll");

    cp.ReferencedAssemblies.Add(
    "System.Data.dll");

    cp.ReferencedAssemblies.Add(
    "System.Xml.dll");

    // cp.ReferencedAssemblies.Add(Com.ClassDir + "ComFun_cs.dll");

    CompilerResults cr
    =new CSharpCodeProvider()

    .CompileAssemblyFromSource(cp,
    string.

    Format(
    @"using System;

    using System.Text.RegularExpressions;

    using System.Collections.Generic;

    using System.Data.SqlClient;

    using System.Xml;

    sealed class {0}{{

    XmlDocument xdoc;

    XmlNamespaceManager nsmgr;

    public bool {1}(){{

    {2}

    }}

    {3}

    }}
    ",

    className, methodName, expression, appcom.ValidateComFuns)
    + Com.GetComFunInfo());



    instance
    = cr.CompiledAssembly.CreateInstance(className);

    method
    = instance.GetType().GetMethod(methodName);



    {

    MethodInfo SFun
    = instance.GetType().GetMethod("Initial");

    if (SFun !=null)

    {

    SFun.Invoke(instance,
    newobject[] { AppCom.XDoc, AppCom.nsmgr });

    }

    }

    CLog.Win_ClassExp.AppendText(appcom.ValidateComFuns);

    }



    // validate one validation method

    publicbool Compute()

    {

    return (bool)method.Invoke(instance, null);

    }

     

     

     

    其中

    sealed class {0}{{

                             XmlDocument xdoc;

                             XmlNamespaceManager nsmgr;

                             public bool {1}(){{

                                {2}

                             }}

                             {3}

                          }}

     

    就是我们运行时从XML动态生成的类,从XML动态传入的参数,返回一个True False的验证结果。参数分别对应如下:

    {0}className 类名

    {1}methodName 方法名

    {2}expression 动态验证表达式

    {3}:动态加入的调用代码

     

     

    很遗憾,这是在标准类库的快捷方便的写法,但是到了Silverlight微缩类库这里,很多类已不存在了。CodeDomProvider类没有了,Microsoft.VisualBasic

    命名空间没有了,CompilerParameters 类没有了,CompilerResults类没有了,Microsoft.CSharp.CSharpCodeProvider类没有了,Microsoft.CSharp

    下还有2个类:Microsoft.CSharp.RuntimeBinder. RuntimeBinderException类和Microsoft.CSharp.RuntimeBinder.RuntimeBinderInternalCompilerException,很明显这2个类是处理异常的。真的要绝望了。

     

    经过捣腾System.Reflection.Assembly类,我们发现有4个方法:

                System.Reflection.Assembly.GetCallingAssembly();

                System.Reflection.Assembly.GetExecutingAssembly();

                System.Reflection.Assembly.Load(assemblyName);

                System.Reflection.Assembly.LoadFrom(assemblyFile);

     

    发现Silverlight中的反射与.NET FRAMEWORK中的略有不同。以下分三种情况描述:

     

         1:动态创建当前执行的程序集中的类实例。

         2:动态创建XAP包中其它SILVERLIGHT程序集中的类实例。

         3:动态下载并创建网站上其它SILVERLIGHT程序集中的类实例。

     

    我们可以利用后两种方法来动态加载程序集,CSharpCodeProvider那种方便快捷的写法不可能用了,于是只能用System.Reflection.Emit 命名空间,定义动态程序集,关于这方面,微软有详细介绍:

    http://msdn.microsoft.com/zh-cn/library/4xtysk39%28v=VS.95%29.aspx

     

    首先需要了解生成方法体的 MSIL 指令代码,感觉和大学学的汇编差不多,

    http://msdn.microsoft.com/zh-cn/library/system.reflection.emit.opcodes%28v=VS.95%29.aspx

     

    贴一个通过Emit动态生成程序集的例子。其实是动态生成了这个类:

    public class MyDynamicType

                {

                    private int m_number;

     

                    public MyDynamicType() : this(42) {}

                    public MyDynamicType(int initNumber)

                    {

                        m_number = initNumber;

                    }

     

                    public int Number

                    {

                        get { return m_number; }

                        set { m_number = value; }

                    }

     

                    public int MyMethod(int multiplier)

                    {

                        return m_number * multiplier;

                    }

                }

     

     

       

    View Code
    class Example

    {

    publicstaticvoid Demo(System.Windows.Controls.TextBlock outputBlock)

    {



    AssemblyName aName
    =new AssemblyName("DynamicAssemblyExample");

    AssemblyBuilder ab
    =

    AppDomain.CurrentDomain.DefineDynamicAssembly(

    aName,

    AssemblyBuilderAccess.Run);



    // Create the module.

    ModuleBuilder mb
    = ab.DefineDynamicModule(aName.Name);



    TypeBuilder tb
    = mb.DefineType(

    "MyDynamicType",

    TypeAttributes.Public);



    // Add a private field of type int (Int32).

    FieldBuilder fbNumber
    = tb.DefineField(

    "m_number",

    typeof(int),

    FieldAttributes.Private);



    // Define a constructor that takes an integer argument and

    // stores it in the private field.

    Type[] parameterTypes
    = { typeof(int) };

    ConstructorBuilder ctor1
    = tb.DefineConstructor(

    MethodAttributes.Public,

    CallingConventions.Standard,

    parameterTypes);



    ILGenerator ctor1IL
    = ctor1.GetILGenerator();

    // For a constructor, argument zero is a reference to the new

    // instance. Push it on the stack before calling the base

    // class constructor. Specify the default constructor of the

    // base class (System.Object) by passing an empty array of

    // types (Type.EmptyTypes) to GetConstructor.

    ctor1IL.Emit(OpCodes.Ldarg_0);

    ctor1IL.Emit(OpCodes.Call,

    typeof(object).GetConstructor(Type.EmptyTypes));

    // Push the instance on the stack before pushing the argument

    // that is to be assigned to the private field m_number.

    ctor1IL.Emit(OpCodes.Ldarg_0);

    ctor1IL.Emit(OpCodes.Ldarg_1);

    ctor1IL.Emit(OpCodes.Stfld, fbNumber);

    ctor1IL.Emit(OpCodes.Ret);



    // Define a default constructor that supplies a default value

    // for the private field. For parameter types, pass the empty

    // array of types or pass null.

    ConstructorBuilder ctor0
    = tb.DefineConstructor(

    MethodAttributes.Public,

    CallingConventions.Standard,

    Type.EmptyTypes);



    ILGenerator ctor0IL
    = ctor0.GetILGenerator();

    // For a constructor, argument zero is a reference to the new

    // instance. Push it on the stack before pushing the default

    // value on the stack, then call constructor ctor1.

    ctor0IL.Emit(OpCodes.Ldarg_0);

    ctor0IL.Emit(OpCodes.Ldc_I4_S,
    42);

    ctor0IL.Emit(OpCodes.Call, ctor1);

    ctor0IL.Emit(OpCodes.Ret);



    // Define a property named Number that gets and sets the private

    // field.

    //

    // The last argument of DefineProperty is null, because the

    // property has no parameters. (If you don't specify null, you must

    // specify an array of Type objects. For a parameterless property,

    // use the built-in array with no elements: Type.EmptyTypes)

    PropertyBuilder pbNumber
    = tb.DefineProperty(

    "Number",

    PropertyAttributes.HasDefault,

    typeof(int),

    null);



    // The property "set" and property "get" methods require a special

    // set of attributes.

    MethodAttributes getSetAttr
    = MethodAttributes.Public |

    MethodAttributes.SpecialName
    | MethodAttributes.HideBySig;



    // Define the "get" accessor method for Number. The method returns

    // an integer and has no arguments. (Note that null could be

    // used instead of Types.EmptyTypes)

    MethodBuilder mbNumberGetAccessor
    = tb.DefineMethod(

    "get_Number",

    getSetAttr,

    typeof(int),

    Type.EmptyTypes);



    ILGenerator numberGetIL
    = mbNumberGetAccessor.GetILGenerator();

    // For an instance property, argument zero is the instance. Load the

    // instance, then load the private field and return, leaving the

    // field value on the stack.

    numberGetIL.Emit(OpCodes.Ldarg_0);

    numberGetIL.Emit(OpCodes.Ldfld, fbNumber);

    numberGetIL.Emit(OpCodes.Ret);



    // Define the "set" accessor method for Number, which has no return

    // type and takes one argument of type int (Int32).

    MethodBuilder mbNumberSetAccessor
    = tb.DefineMethod(

    "set_Number",

    getSetAttr,

    null,

    new Type[] { typeof(int) });



    ILGenerator numberSetIL
    = mbNumberSetAccessor.GetILGenerator();

    // Load the instance and then the numeric argument, then store the

    // argument in the field.

    numberSetIL.Emit(OpCodes.Ldarg_0);

    numberSetIL.Emit(OpCodes.Ldarg_1);

    numberSetIL.Emit(OpCodes.Stfld, fbNumber);

    numberSetIL.Emit(OpCodes.Ret);



    // Last, map the "get" and "set" accessor methods to the

    // PropertyBuilder. The property is now complete.

    pbNumber.SetGetMethod(mbNumberGetAccessor);

    pbNumber.SetSetMethod(mbNumberSetAccessor);



    // Define a method that accepts an integer argument and returns

    // the product of that integer and the private field m_number. This

    // time, the array of parameter types is created on the fly.

    MethodBuilder meth
    = tb.DefineMethod(

    "MyMethod",

    MethodAttributes.Public,

    typeof(int),

    new Type[] { typeof(int) });



    ILGenerator methIL
    = meth.GetILGenerator();

    // To retrieve the private instance field, load the instance it

    // belongs to (argument zero). After loading the field, load the

    // argument one and then multiply. Return from the method with

    // the return value (the product of the two numbers) on the

    // execution stack.

    methIL.Emit(OpCodes.Ldarg_0);

    methIL.Emit(OpCodes.Ldfld, fbNumber);

    methIL.Emit(OpCodes.Ldarg_1);

    methIL.Emit(OpCodes.Mul);

    methIL.Emit(OpCodes.Ret);



    // Finish the type.

    Type t
    = tb.CreateType();



    // The code can be executed immediately. Start by getting reflection

    // objects for the method and the property.

    MethodInfo mi
    = t.GetMethod("MyMethod");

    PropertyInfo pi
    = t.GetProperty("Number");



    // Create an instance of MyDynamicType using the default

    // constructor.

    object o1 = Activator.CreateInstance(t);



    // Display the value of the property, then change it to 127 and

    // display it again. Use null to indicate that the property

    // has no index.

    outputBlock.Text
    += String.Format("o1.Number: {0}\n", pi.GetValue(o1, null));

    pi.SetValue(o1,
    127, null);

    outputBlock.Text
    += String.Format("o1.Number: {0}\n", pi.GetValue(o1, null));



    // Call MyMethod, passing 22, and display the return value, 22

    // times 127. Arguments must be passed as an array, even when

    // there is only one.

    object[] arguments = { 22 };

    outputBlock.Text
    += String.Format("o1.MyMethod(22): {0}\n",

    mi.Invoke(o1, arguments));



    // Create an instance of MyDynamicType using the constructor

    // that specifies m_Number. The constructor is identified by

    // matching the types in the argument array. In this case,

    // the argument array is created on the fly. Display the

    // property value.

    object o2 = Activator.CreateInstance(t,

    newobject[] { 5280 });

    outputBlock.Text
    += String.Format("o2.Number: {0}\n", pi.GetValue(o2, null));

    }

    }

     

    好了,原理清晰了,一起看看在Siverlight上实现的例子吧(一定要看最后一节的“动态自定义验证”),感谢大家。

     

    关于作者: 王昕(QQ:475660) 在广州工作生活30余年。十多年开发经验,在Java、即时通讯、NoSQL、BPM、大数据等领域较有经验。
    目前维护的开源产品:https://gitee.com/475660
  • 相关阅读:
    优雅的将hbase的数据导入hive表
    大数据技术-spark+hive+hbase研究
    第0001课
    线程池-实际生产使用
    H5学习系列之文件读取API--本文转自http://blog.csdn.net/jackfrued/article/details/8967667
    H5学习系列之Communication API
    H5学习系列之Geolocation API
    H5学习系列之Audio和Video
    H5学习系列之webSocket入门
    百度UEditor基本使用
  • 原文地址:https://www.cnblogs.com/starcrm/p/2090288.html
Copyright © 2011-2022 走看看