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<stringstring> GetItemDescriptionAndCode(Guid itemID)

        {

            Dictionary<stringstring> itemDesList = new Dictionary<stringstring>();

            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<stringTypedaoDic = new Dictionary<stringType >();

        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/archive/2012/01/11/2319810.html,如需转载请自行联系原作者

相关文章
|
22天前
|
编译器 C#
c# - 运算符<<不能应用于long和long类型的操作数
在C#中,左移运算符的第二个操作数必须是 `int`类型,因此需要将 `long`类型的位移计数显式转换为 `int`类型。这种转换需要注意数据丢失和负值处理的问题。通过本文的详细说明和示例代码,相信可以帮助你在实际开发中正确使用左移运算符。
31 3
|
21天前
|
编译器 C#
c# - 运算符<<不能应用于long和long类型的操作数
在C#中,左移运算符的第二个操作数必须是 `int`类型,因此需要将 `long`类型的位移计数显式转换为 `int`类型。这种转换需要注意数据丢失和负值处理的问题。通过本文的详细说明和示例代码,相信可以帮助你在实际开发中正确使用左移运算符。
37 1
|
20天前
|
编译器 C#
c# - 运算符<<不能应用于long和long类型的操作数
在C#中,左移运算符的第二个操作数必须是 `int`类型,因此需要将 `long`类型的位移计数显式转换为 `int`类型。这种转换需要注意数据丢失和负值处理的问题。通过本文的详细说明和示例代码,相信可以帮助你在实际开发中正确使用左移运算符。
11 0
|
1月前
|
C#
C# 可空类型(Nullable)
C# 单问号 ? 与 双问号 ??
46 12
|
1月前
|
数据可视化 程序员 C#
C#中windows应用窗体程序的输入输出方法实例
C#中windows应用窗体程序的输入输出方法实例
46 0
|
3月前
|
程序员 C#
C# 语言类型全解
C# 语言类型全解
25 0
|
3月前
|
开发框架 .NET 编译器
C# 中的记录(record)类型和类(class)类型对比总结
C# 中的记录(record)类型和类(class)类型对比总结
|
6月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
198 3
|
17天前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
30 3
|
1月前
|
安全 C# 数据安全/隐私保护
实现C#编程文件夹加锁保护
【10月更文挑战第16天】本文介绍了两种用 C# 实现文件夹保护的方法:一是通过设置文件系统权限,阻止普通用户访问;二是使用加密技术,对文件夹中的文件进行加密,防止未授权访问。提供了示例代码和使用方法,适用于不同安全需求的场景。
109 0