• c# dynamic 类型调用静态方法实例


     

    /玄魂

    背景

    最近一直在和同事讨论单元测试的问题,在对已有代码的可测试性进行评估的时候,我们发现业务逻辑层和持久层的测试分离成为了难点。

    正常而言,对业务逻辑的单元测试是要同持久层分离开的。为了确保业务逻辑层的可测试性,要求业务逻辑层依赖持久层的接口而不是实现,这样在进行单元测试的时候,可以灵活的使用Mock和数据库来填充数据。

    但是我们的代码规范规定,Dao层的方法必须是静态方法,而且之前的业务逻辑代码在逻辑内部调用Dao,二者紧紧的耦合在一起。现在面临的问题是Dao层的方法必须是静态方法,我们没有办法提取接口。初步讨论,为了达到可测试性,有以下几个改造方案:

    l  将业务逻辑层调用的Dao类提取出公有变量,然后调用方实施属性注入或者构造函数注入的方式。实现了业务逻辑的可测试性,但是没有实现业务逻辑和持久层的解耦。

    l  新建接口封装对Dao的调用。实现了可测试性,也实现了业务逻辑和持久层的解耦,但是违反了Dao方法必须静态的初衷,如果不考虑单元测试,接口包装的方式在设计上显得臃肿,而且改造起来代码量大。

    l  使用dynamic类型声明Dao类,在业务逻辑的构造工厂中依赖注入实现。这种方法对代码的改动量最小,但是失去了编译时检查的优势;同时每种调用类型都是事先知道的,不是dynamic类型的标准应用;这里使用dynamic类型只是利用它的运行时绑定的特性,实现类似接口的功能,不得已而为之。

    总之,如果不实现真正的解耦,任何方案都是勉强。本篇文章讨论上述最后一种方案的实施过程中遇到的一个dynamic 类型变量调用静态方法的解决方案,同时兼顾单元测试,和分层解耦。这种方案也不是我的原创,参考链接:http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx

    dynamic 类型调用静态方法

    我先模拟一个Dao的实现,类名为ReportItemDao,只有一个方法,名为GetItemDescriptionAndCode,如下:

    public class ReportItemDao

        {

        /// <summary>

            /// 根据条目ID获取条目描述列表

            /// </summary>

            /// <param name="itemID">条目ID</param>

            /// <returns>当前条目的描述列表</returns>

            /// <remarks>玄魂-2012-1-11创建</remarks>

            public static Dictionary<string, string> GetItemDescriptionAndCode(Guid itemID)

            {

                Dictionary<string, string> itemDesList = new Dictionary<string, string>();

                Database database = Database.GetDatabase(StaticParameters.CONNECTIONSTRINGS_NAME_Design);

                SafeProcedure.ExecuteAndGetInstanceList(database, @"[dbo].[GetReportTempDesByItemID]",

                                                    parameters =>

                                                    {

                                                        parameters.AddWithValue(@"itemID", itemID);

                                                    },

                                                    (IRecord record, int entity) =>

                                                    {

                                                        itemDesList.Add(record.Get<string>(@"Code"), record.Get<string>(@"ReportItemDecription"));

                                                    }

                                                    );

                return itemDesList;

            }

    }

    再模拟一个业务逻辑层的代码,调用上面的方法,如下:

    public class ReportItemProvider : IReportItemProvider

        {

     

    public Dictionary<string, string> GetItemDescriptionAndCode(Guid itemID)

            {

                return ReportItemDao.GetItemDescriptionAndCode(itemID);

            }

    }

    上面的调用代码也很简单,没有任何逻辑,实际场景下会复杂得多。首先,在ReportItemProvider类声明一个dynamic字段,名为ReportItemDao,取消对ReportItemDao类的命名空间的引入。修改之后的代码如下:

    public class ReportItemProvider : IReportItemProvider

    {

    dynamic ReportItemDaoDynamic

     

    public Dictionary<string, string> GetItemDescriptionAndCode(Guid itemID)

            {

                return ReportItemDaoDynamic.GetItemDescriptionAndCode(itemID);

            }

    }

    上面的代码是理想状态,并不能运行成功,因为我们无法将ReportItemDao类赋值给dynamic类型,实例化的类型是无法调用静态方法的。

    ReportItemDao类的实例不能赋值给ReportItemDaoDynamic,那我们只能传一个ReportItemDaoType类实例给ReportItemDaoDynamic,别无他法。传递一个Type类实例和dynamic类型,意味着在执行具体方法时必须执行反射,但是dynamic类型目前还不支持这样的调用,我们必须对它的调用过程进行重写。

    下面的代码实现了对dynamic类型的自定义。先创建一个名为StaticMembersDynamicWrapper的类,继承自DynamicObject类,然后重写它的TryGetMemberTryInvokeMember方法,利用反射找到静态方法并执行。

    public class StaticMembersDynamicWrapper : DynamicObject {

    private Type _type;

    public StaticMembersDynamicWrapper(Type type) { _type = type; }

    // Handle static properties

    public override bool TryGetMember(GetMemberBinder binder, outobjectresult) {

          PropertyInfo prop = _type.GetProperty(binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);

          if(prop == null) {

                           result = null;

                           returnfalse;

                          }

           result = prop.GetValue(null, null);

           returntrue;

    }

    // Handle static methods

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, outobjectresult) {

            MethodInfo method = _type.GetMethod(binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);

            if(method == null) {

                                result = null;

                                 returnfalse;

                                }

           result = method.Invoke(null, args);

           returntrue;

         }

    }

    dynamic 类型调用静态方法的问题解决了,还需要一个对象工厂对dynamic 类型的变量进行依赖注入。

    在依赖注入之前,我还要一个简单的Ioc容器来存储Dao类的Type,代码如下:

     public  static class DaoContainer

        {

            private static Dictionary<string, Type> daoDic = new Dictionary<string, Type >();

            static DaoContainer ()

            {

                daoDic.Add(“ReportItemDaoDynamic”,typeOf(ReportItemDao));        

            }

     

      public static Type  GetTypeInstancestring key

    {

      return daoDic[key];

    }

    }

    下面我们用一个简单的工厂类来创建ReportItemProvider

     class ProviderFactory

        {

          public T GetInstance<T>() where T : class

            {

           var instance = ReportItemProvider.Instance;//单例

                SetDao(instance);

                          return instance;

            }

    }

    private void SetDao(object obj)

    {

       Type type = obj.GetType();

               FieldInfo[]fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);

               foreach (FieldInfo field in fields)

               {

                   if (field.GetValue(obj)==null)

                   {

    //这里判断是否应该赋值,省略……

                       string name = field.Name;

                     field.SetValue(obj, DaoContainer. GetTypeInstance(name));

                   }

               }

    }

    ok,目前为止我已经给出了一个极其简单但是五脏俱全的例子了,当然这可能不是最好的解决方案。希望对您能有所帮助。


    作者:玄魂
    出处:http://www.cnblogs.com/xuanhun/
    原文链接:http://www.cnblogs.com/xuanhun/ 更多内容,请访问我的个人站点 对编程,安全感兴趣的,加qq群:hacking-1群:303242737,hacking-2群:147098303,nw.js,electron交流群 313717550。
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    关注我:关注玄魂的微信公众号

  • 相关阅读:
    linux 用户、组,修改文件权限
    linux下获取帮助
    PHPSESSID的cookie//session_start()
    【python】import 模块、包、第三方模块
    python练习——最长的递减子序列
    python练习——水仙花数
    Linux目录结构
    Scala入门3(特质线性化)
    Scala入门2(特质与叠加在一起的特质)
    人工智能我见及特征提取mfcc算法理解
  • 原文地址:https://www.cnblogs.com/xuanhun/p/2319810.html
Copyright © 2020-2023  润新知