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,目前為止我已經給出了一個極其簡單但是五臟俱全的例子了,當然這可能不是最好的解決方案。希望對您能有所幫助。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM