使用反射和codeDom实现C#插件开发(4)主程序开发之main函数编写

时间:2022-07-20 16:07:09
编写代码的最后一部分,主程序。
Main函数中代码主要分为几块,1)找到可以被系统调用的程序集;2)找到可以被交互调用的类;3)创建类的实例


;4)获取输入参数;5)修改默认的参数;6)动态生成类库;7)调用动态生成的类库,执行赋值操作;8)执行插


件中的函数。


我把代码按照上面步骤截断,代码如下:
1)找到可以被系统调用的程序集;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Collections;
using 标记属性;


namespace _0331使用动态类和反射设置某个类中的变量值
{
    class Program
    {
//创建一个全局变量,保存通过界面输入的参数,并将该参数传给动态类,完成参数赋值
        static List<programData> inputDataList = new List<programData>();


        static void Main(string[] args)
        {
            Type interactionClass=null;
            string dllName = "0331测试的Person类.dll";
            string fullInteractClassName = "";


            #region 找到可以系统调用的程序集名称
            //是否可以被系统集成
            Assembly personAsm = Assembly.LoadFrom(dllName);
            Attribute attr = Attribute.GetCustomAttribute(personAsm, typeof(canUsedBySystem));
            if (attr == null)
            {
                return;
            }
            #endregion


2)找到可以被交互调用的类


#region 找到可以被交互调用的类,并获取类的全名 namespace.class
            Type[] ts = personAsm.GetTypes();
            foreach (Type t in ts)
            {
                Attribute inputAttr = Attribute.GetCustomAttribute(t, typeof(AInteraction));
                if (inputAttr != null)
                {
                    interactionClass = t;
                    fullInteractClassName = t.FullName;
                }
                else
                    interactionClass = null;
            }
            #endregion


3)创建类的实例


//创建类的实例,下面获取参数、设置参数、求解计算的部分都共用这个实例
            object obj = Activator.CreateInstance(interactionClass);


4)获取输入参数
//获取输入参数
            getINParams(obj);


在这里,我把它放到一个函数里。
static void getINParams(object t)
        {
            inputDataList.Clear();


            FieldInfo[] fi = t.GetType().GetFields();       //获取所有field
            StringBuilder sb = new StringBuilder();
            foreach (FieldInfo f in fi)
            {


                //根据属性判断,是否是输入参数
                Attribute assAttr = Attribute.GetCustomAttribute(f, typeof(AIsInput));             
                if (assAttr != null)
                {
                    string name = f.Name;       //参数名
                    programData data = new programData();
                    data.参数名称 = name;
                    sb.Append(name + "----;type is ");


                    Type ftp = f.FieldType;  //参数类型
                    data.参数类型 = ftp.Name;
                    sb.Append(ftp.Name + "----;default value is ");


                    //遍历各个参数中的默认值,目前参数可以设置为object,或者object[]
                    IList<CustomAttributeData> datalist = f.GetCustomAttributesData();
                    if (datalist.Count != 0)
                    {
                        foreach (CustomAttributeData da in datalist)
                        {
                            if (da.Constructor.DeclaringType.Name == "ADefaultValues")
                            {
                                string values = "";
                                Type tt = da.ConstructorArguments[0].ArgumentType;
                                object x = da.ConstructorArguments[0].Value;
                                if (tt.Name == "Object[]")
                                {
                                    


System.Collections.ObjectModel.ReadOnlyCollection<System.Reflection.CustomAttributeTypedArgument> sa 





(System.Collections.ObjectModel.ReadOnlyCollection<System.Reflection.CustomAttributeTypedArgument>)x;
                                    for (int i = 0; i < sa.Count; i++)
                                    {
                                        x = sa[i].Value;
                                        values += x.ToString() + ",";
                                        sb.Append(x.ToString() + ",");
                                    }
                                }
                                else
                                {// 单值            
                                    values = x.ToString();
                                    sb.Append(x.ToString());
                                }
                                data.参数值 = values;
                            }


                            if (da.Constructor.DeclaringType.Name == "AMessage")
                            {
                                object x = da.ConstructorArguments[0].Value;
                                data.参数描述 = x.ToString();
                            }
                        }
                    }
                    sb.Append("\n");
                    inputDataList.Add(data);
                }
            }
        }


5)修改默认的参数


 #region 在主程序中设置参数
            // 可以通过界面进行设置参数
            Console.WriteLine("我的名字:");
            inputDataList[0].参数值 =Console.ReadLine();


            Console.WriteLine("我的年龄:");
            inputDataList[1].参数值 =Console.ReadLine();


            Console.WriteLine("男?True or False:");
            inputDataList[3].参数值 = Console.ReadLine();
           
            Console.WriteLine("从事工作有哪些?");
            inputDataList[4].参数值 = Console.ReadLine();


            Console.WriteLine("我的资产:");
            inputDataList[2].参数值 = Console.ReadLine();
            
            #endregion




6)动态生成类库


 // 动态生成包含执行参数设置函数的类
            genDynamicDll(fullInteractClassName, dllName);


这里我也放到一个函数里了。
static void genDynamicDll(string fullClassName,string dllname)
        {
            // 获取插件类的名称
            string filenm = "..\\..\\dynamicCode.cs"; //将代码写到一个文件里。当然也可以直接写到内


存里或一个流里面,比如StringWriter里面---  StringWriter sw = new StringWriter();
            
            //将所有参数分解到一个ArrayList里面,传入动态类。(暂且这么用,应该有更好的方式)
            ArrayList arr = new ArrayList();
            foreach (programData data in inputDataList)
            {
                if (data.参数值 == null)
                    continue;
                arr.Add(data.参数名称);
                arr.Add(data.参数类型);
                if (data.参数类型 == "Double")
                    arr.Add(Convert.ToDouble(data.参数值.Trim()));
                else if (data.参数类型 == "Int32")
                    arr.Add(Convert.ToInt32(data.参数值.Trim()));
                else if (data.参数类型 == "Boolean")
                    arr.Add(Convert.ToBoolean(data.参数值.Trim()));
                else if (data.参数类型 == "String")
                    arr.Add(data.参数值);
                else if (data.参数类型 == "Int32[]")
                    arr.Add(data.参数值);
                else if (data.参数类型 == "String[]")
                    arr.Add(data.参数值);
                else
                {
                }
            }
            dynamic.generateCode(filenm, fullClassName, arr);


            CompilerResults crt = dynamic.compileCode(filenm, dllname);


#if DEBUG   //输出一些调试信息
            if (crt.Errors.Count > 0)
            {
                for (int i = 0; i < crt.Output.Count; i++)
                    Console.WriteLine(crt.Output[i]);
                for (int i = 0; i < crt.Errors.Count; i++)
                    Console.WriteLine(i.ToString() + ": " + crt.Errors[i].ToString());
            }
            else
            {
                Console.WriteLine("compile ok");
            }
#endif
        }






7)调用动态生成的类库,执行赋值操作


 #region 调用动态生成的dll,执行赋值操作;因为这个代码是你写的,所以dll名称及内部信息都知道


            Assembly dynamicDll = Assembly.LoadFrom("dynamic.dll");    
            Type dynamicClass = dynamicDll.GetType("dynamicNS.dynamicClass");   
            object dynamicObj = Activator.CreateInstance(dynamicClass);


            //调用函数
            MethodInfo setm = dynamicClass.GetMethod("setValues", new Type[] { interactionClass });
            setm.Invoke(dynamicObj, new object[] { obj });
            #endregion


8)执行插件中的函数。


 #region 设置makeSomeMoney函数的参数列表;
            Console.WriteLine("我想赚到:");
            double doller=Convert.ToDouble(Console.ReadLine());
            #endregion


            #region 执行Person类中的函数


            MethodInfo[] methods = interactionClass.GetMethods();
            foreach (MethodInfo m in methods)
            {
                if (m.Name == "makeSomeMoney")      //肯定有这个函数,因为在接口中定义了
                {
                    m.Invoke(obj, new object[] { doller }); // 传入doller参数,执行makeSomeMoney函数  
                    continue;
                }
                if (m.Name == "showMsg")        // 肯定有这个函数,因为在接口中定义了
                {
                    m.Invoke(obj, new object[] { "主函数" });   //执行showMsg函数
                    continue;
                }


                //通过此标志获取所有可以使用的函数,但函数名还得约定好
                Attribute cattr = Attribute.GetCustomAttribute(m, typeof(AcanRun));     
                if (cattr != null)
                {
                    m.Invoke(obj,null);
                    continue;
                }           
            }
            #endregion 
            
            Console.ReadKey();
        }


整个代码编写完成了,可以进行测试了。




上面整个过程测试下来,还是花费了很多时间,查了很多资料。里面还有很多需要研究的,比如,各个类型变量的


定义,输出变量如何不使用static,也能取出来扥等。


可能这个方法实在是笨拙。不过,好歹也满足了我的工程需要。


写下这个来,一方面是因为在测试过程中一点一点突破,还有些晕晕乎乎,想再好好整理一下这个流程。另一方面


,还想请大家看看,在C#范围内,有没有更好的方法能够实现这个功能呢?