2010-01-29 143 views
-1

我嘗試寫一些代碼,以幫助單元測試WCF服務。這些服務通過創建代理實例的Facade類訪問,然後調用代理方法並返回結果;爲每個代理方法。我希望能夠用創建真實服務或虛假服務的東西替換當前的創建代碼。泛型約束與傳承

我不能得到那個工作。我煮它歸結爲以下幾點:

using System.ServiceModel; 

namespace ExpressionTrees 
{ 
    public interface IMyContract 
    { 
     void Method(); 
    } 

    public class MyClient : ClientBase<IMyContract>, IMyContract 
    { 
     public MyClient() 
     { 
     } 

     public MyClient(string endpointConfigurationName) 
      : base(endpointConfigurationName) 
     { 
     } 

     public void Method() 
     { 
      Channel.Method(); 
     } 
    } 

    public class Test 
    { 
     public TClient MakeClient<TClient>() 
      where TClient : ClientBase<IMyContract>, IMyContract, new() 
     { 
      return new MyClient("config"); 

      // Error: 
      // Cannot convert expression of type 'ExpressionTrees.ServiceClient' to return type 'TClient' 
     } 
    } 
} 

爲什麼說,即使MyClient類從ClientBase<IMyContract>派生並實現IMyContract,我不能想回一個TClient的方法返回一個MyClient實例? TClient指定了一個類型約束,我認爲這意味着同樣的事情。


我的目標是調用代碼:

public void CallClient<TClient>() 
     where TClient : ClientBase<IMyContract>, IMyContract 
    { 
     TClient client = null; 
     bool success = false; 
     try 
     { 
      client = MakeClient<TClient>(); 
      client.Method(); 
      client.Close(); 
      success = true; 
     } 
     finally 
     { 
      if (!success && client != null) 
      { 
       client.Abort(); 
      } 
     } 
    } 

但不是始終在呼喚MakeClient<TClient>,我希望能夠有一個單元測試注入模擬對象。由於上面的代碼取決於ClientBase<IMyContract>,IMyContract,所以我似乎在試圖「合成」一個能夠滿足這個約束的泛型類。

現在回想起來,這是沒有意義的。作爲一個例子,所述ClientBase<IMyContract>會期望在一個Channel對象將被構造,它可以然後委託Close方法這樣的方式來進行實例化。

我已經結束了撐船具有完全相同的代碼運行無論對於真實和虛假服務。我現在正在注入一個IMyService,並調用IMyService.Methodclient.Method,具體取決於我的注入屬性是否爲空。

+1

我仍然無法想象爲什麼這個問題吸引了downvotes。我希望其中一位下臺者會告訴我,以便我可以改進這個問題。 – 2013-12-21 01:59:28

回答

8

基本上你的代碼歸結爲:

public static T MakeFruit<T>() where T : Fruit 
    { 
     return new Apple(); 
    } 

這總是返回一個蘋果,即使你打電話MakeFruit<Banana>()。但MakeFruit<Banana>()需要返回一個香蕉,而不是一個蘋果。

泛型類型約束的含義是,通過調用者提供的類型參數必須與約束相匹配。因此,在我的示例中,可以說MakeFruit<Banana>(),但不是MakeFruit<Tiger>(),因爲Tiger不符合T必須可轉換爲Fruit的約束條件。我認爲你相信約束意味着別的東西;我不確定那是什麼。

想想看這樣的。一個形式參數有一個正式的參數類型。形式參數類型限制用作參數的表達式的類型。所以,當你說:

void M(Fruit x) 

你說「在併購正式參數x傳遞的參數必須轉換到果子」。

通用類型參數約束是完全相同的;它們是對通用類型參數可能傳遞什麼類型參數的限制。當你說「Where T:Fruit」時,這與正式參數列表中的(Fruit x)相同。 T必須成爲Fruit的一種類型,就像x的論點必須成爲Fruit的論點一​​樣。

爲什麼你連想都擺在首位的通用方法是什麼?我不明白你試圖用這種方法建模的原因,或者你爲什麼希望它是通用的。

+0

埃裏克,我會更深入一步,因此我很清楚自己想要做什麼。 – 2010-01-29 03:08:10

3

您被限制TClient呼叫,而不是返回類型的MakeClient<TClient>()部分。

返回類型必須匹配泛型參數的類型,但拍下這一刻:

public class MyOtherClient : ClientBase<IMyContract>, IMyContract 
{ 
    public void Method() 
    { 
     Channel.Method(); 
    } 
} 

這也是通過調用MakeClient<MyOtherClient>有效的回報,這MyClient沒有可轉化到,因爲它應該返回一個類型的MyOtherClient

注意,更改回:

return new MyClient() as TClient; 

可能會得到它過去的編譯器,但在運行時在我的情況下空之上。

0

這應該可以解決您的問題。

static T Make<T>() where T : IConvertible 
{ 
    var s = ""; 
    return (T)(Object)s;   
} 
+0

不幸的不是。我真的需要一個參數化的構造函數。我更新了我的問題以表明這一點。 – 2010-01-29 01:39:28