2011-05-25 64 views
3

我正在編寫兩個API,我將與許多項目一起使用。有些項目使用了其中一個API,另一些則使用了其中一些,但我的大部分項目都將使用這兩個API。我試圖將它們設計成好像它們完全分開,但我在一件事上掙扎着。具有相同通用方法的抽象類和接口

namespace FirstApi { 
    public abstract class MyBaseClass { 
     //constructor, some methods and properties 

     public IEnumerable<T> Search<T>() where T : MyBaseClass, new() { 
      //search logic here. must use generics as I create new instances of T here 
     } 
    } 
} 


namespace SecondApi { 
    public interface IMyInterface { 
     //some property and method signatures 

     IEnumerable<T> Search<T>() where T : IMyInterface, new(); 
    } 
} 

namespace MyProject { 
    public class MyDerivedClass : MyBaseClass, IMyInterface { 

    } 
} 

這兩個API都需要這種搜索方法。第二個API在調用IMyInterface.Search<T>()的其他類中有一些功能,我希望那些繼承MyBaseClass的類使用MyBaseClass中定義的Search<T>函數。

編譯錯誤:類型參數方法的「T」「MyBaseClass.Search()」的約束必須爲類型參數界面方法的「T」「IMyInterface.Search()」的約束條件相匹配。考慮使用顯式接口實現。

注意:當搜索被調用時,T將永遠是任何抽象類或接口已被繼承的派生類。這是我在C#2.0(C# abstract class return derived type enumerator)中實現這一目標的唯一方法,它只是造成了更多問題!

有沒有一種類型安全的方式,我可以實現這一點,而不使用對象和鑄造?

解決方案:基於由安德拉斯·佐爾坦接受的答案

,我在我的項目創造了這個班,並且將不得不重新創建這個類爲使用這兩個API每個項目。

public abstract class ApiAdapter<TAdapter> : MyBaseClass, IMyInterface where TAdapter: MyBaseClass, IJsonObject, new() 
{ 
    IEnumerable<T> IJsonObject.Search<T>() 
    { 
     foreach (TAdapter row in base.Search<TAdapter>()) 
      yield return (T)(IMyInterface)row; 
    } 
} 

然後我就像這樣繼承這個類。

public class Client : ApiAdapter<Client> { 
    //everything else can go here 
} 
+0

也許它在清晨,我都還沒有甦醒過來,但我不明白問題是什麼。爲什麼你提供的代碼不工作? – 2011-05-25 09:11:29

+0

對不起,我更新了編譯器錯誤的帖子 – Connell 2011-05-25 09:16:46

+0

對不起,我也不明白這個問題。也許提供一個真實世界的例子,而不是'IMyInterface'和'MyBaseClass'可以提供幫助。 – 2011-05-25 09:17:00

回答

2

我開始回答說明爲什麼它不適合你,但我認爲現在已經很好理解了,所以我會把它排除在外。

我已經upvoted @ IndigoDelta的答案,但它突出了一些我不喜歡這裏的整體設計 - 我有一個偷偷摸摸的實際上你應該使用通用接口和泛型類;不是通用的方法,因爲它沒有任何意義:

Note: When Search is called, T will always be the derived class of whichever abstract class or interface has been inherited.

我把這個解決方案投入混合;我認爲這樣更好,因爲這意味着每個派生類型都不需要重新實現IMyInterface.Search方法,並且它可以通過某種方式實際執行您提到的這個規則。這是一個泛型類型,致力於將兩個API一起使用,這意味着派生類型不需要做任何事情:

namespace MyProject 
{ 
    using FirstApi; 
    using SecondApi; 

    public class SecondAPIAdapter<T2> : MyBaseClass, IMyInterface 
    where T2 : SecondAPIAdapter<T2>, new() 
    { 
    #region IMyInterface Members 
    IEnumerable<T> IMyInterface.Search<T>() 
    { 
     return Search<T2>().Cast<T>(); 
    } 
    #endregion 
    } 

    //now you simply derive from the APIAdapter class - passing 
    //in your derived type as the generic parameter. 
    public class MyDerivedClass : SecondAPIAdapter<MyDerivedClass> 
    { } 
} 
+0

這看起來像是理想的解決方案!這也意味着我可以保持兩個API的設計完整。我會走開,試試這個,回到你身邊。 – Connell 2011-05-25 10:31:13

+0

我喜歡這個,比我發佈的解決方案更優雅,並且會防止重複的代碼。 – IndigoDelta 2011-05-25 12:10:37

1

我認爲你可以做顯式的接口實現,當你通過IMyInterface.Search訪問方法時,編譯器會運行正確的方法。

4

您可以明確地實現接口搜索方法,例如

public class MyDerivedClass : BasicTestApp.FirstApi.MyBaseClass, BasicTestApp.SecondApi.IMyInterface 
    { 
     IEnumerable<T> SecondApi.IMyInterface.Search<T>() 
     { 
      // do implementation 
     } 
    } 

不過,我覺得你問的MyBaseClass搜索方法時,處理你的對象IMyInterface的部分代碼調用Search<T>方法被調用。我看不到方法,因爲您有兩個具有不同約束條件的T類型,無法關聯。 如果您在搜索方法的兩個定義中都做了where T : BasicTestApp.FirstApi.MyBaseClass, IMyInterface, new();,那麼您不會遇到問題,但這會將兩個API綁定在一起

以下是您明確實現的接口方法的可能實現。它不能避免演員陣容,但至少保持整潔。

 IEnumerable<T> SecondApi.IMyInterface.Search<T>() 
     { 
      var results = base.Search<MyDerivedClass>(); 

      return results.Cast<T>(); 
     } 
+0

+1這是我要去做的事情 - 但我試圖找到一種不固定類型的方法 - 如果可能的話,保持它與輸入類型相同。因爲對於一個通用函數來說,它總是沒有意義的。這對我來說聽起來像一個普通的類。 – 2011-05-25 09:46:31

+0

我想我喜歡它+1,但是,爲什麼不實現基類上的接口? – Jodrell 2011-05-25 09:56:18

+0

@Jodrell我相信這是因爲FirstAPI不知道SecondAPI的任何內容 – 2011-05-25 10:01:49

1

您需要使用顯式實現。

public class MyDerivedClass : MyBaseClass, IMyInterface 
{ 
    // The base class implementation of Search inherited 

    IEnumerable<T> IMyInterface.Search<T>() 
    { 
     // The interface implementation 
     throw new NotImplementedException(); 

     // this would not work because base does not implement IMyInterface 
     return base.Search<T>(); 
    }  
} 

由於實現是不同的這是有道理的。如果它們不相同,那麼基類應該實現接口,並且應該使用協變(.Net 4)。0)來結合你的約束,或者,你可能根本不需要接口。

0

我希望我不是糊塗了,你能不能改變你的定義,使得:

public interface IMyInterface<in T> 
{ 
    //some property and method signatures 

    IEnumerable<U> Search<U>() where U : T, new(); 
} 

提供了T一個通用的參數,它可以用來強制執行的實現提供了搜索功能,約束類型的T

public abstract class MyBaseClass : IMyInterface<MyBaseClass> 
{ 
    public virtual IEnumerable<T> Search<T>() where T : MyBaseClass, new() 
    { 

    } 
} 

這樣一來,你的派生類型是簡單的:

public class MyDerivedClass : MyBaseClass 
{ 

} 

然後你就可以做搜索爲:

var derived = new MyDerivedClass(); 
IMyInterface<MyDerivedClass> iface = impl; 

var results = iface.Search<MyDerivedClass>(); 
+0

這是一個好主意。通過將我的第二個API接口定義爲'interface IMyInterface 其中T:IMyInterface '並定義派生類,比如'MyDerivedClass:MyBaseClass,IMyInterface ',會有一種方法嗎? – Connell 2011-05-25 10:28:11

相關問題