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
  • 相关阅读:
    无线鼠标换电池了
    Jython Interactive Servlet Console YOU WILL NEVER KNOW IT EXECLLENT!!! GOOD
    Accessing Jython from Java Without Using jythonc
    jython podcast cool isnt't it?
    Python里pycurl使用记录
    Creating an Interactive JRuby Console for the Eclipse Environment
    微软为AJAX和jQuery类库提供CDN服务
    Download A File Using Cygwin and cURL
    What is JMRI?这个是做什么用的,我真没看懂但看着又很强大
    用curl 发送指定的大cookie的http/https request
  • 原文地址:https://www.cnblogs.com/starcrm/p/2090288.html
Copyright © 2011-2022 走看看