[英]Abstract class and interface with the same generic method
我正在編寫兩個 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>
MyBaseClass
。
編譯錯誤:方法“MyBaseClass.Search()”的類型參數“T”的約束必須匹配接口方法“IMyInterface.Search()”的類型參數“T”的約束。 考慮改用顯式接口實現。
注意:當調用 Search 時,T 將始終是繼承的抽象 class 或接口的派生 class。 這是我在 C# 2.0 ( C# abstract class return derived type enumerator ) 中找到的唯一方法,它只是引起了更多問題!
有沒有一種類型安全的方法可以在不使用對象和強制轉換的情況下實現這一點?
解決方案:
根據 Andras Zoltan 接受的答案,我在我的項目中創建了這個 class,並且必須為使用這兩種 API 的每個項目重新創建這個 class。
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;
}
}
然后我像這樣繼承這個 class 。
public class Client : ApiAdapter<Client> {
//everything else can go here
}
您可以顯式實現接口 Search 方法,例如
public class MyDerivedClass : BasicTestApp.FirstApi.MyBaseClass, BasicTestApp.SecondApi.IMyInterface
{
IEnumerable<T> SecondApi.IMyInterface.Search<T>()
{
// do implementation
}
}
但是,我認為您要求在處理您的 object 作為IMyInterface
的代碼部分調用Search<T>
方法時調用MyBaseClass
Search 方法。 我看不到方法,因為您有兩種T
類型,它們具有無法關聯的不同約束。 如果你做了where T: BasicTestApp.FirstApi.MyBaseClass, IMyInterface, new();
在 Search 方法的兩個定義中,您不會有問題,但這會將您的兩個 API 綁定在一起
這是您顯式實現的接口方法的可能實現。 它並沒有避免演員陣容,但至少保持整潔。
IEnumerable<T> SecondApi.IMyInterface.Search<T>()
{
var results = base.Search<MyDerivedClass>();
return results.Cast<T>();
}
我從解釋為什么它不適合你開始我的回答,但我認為現在已經很好理解了,所以我會省略它。
我贊成@IndigoDelta的回答,但它突出了我不喜歡這里整體設計的一些東西——我偷偷懷疑你實際上應該使用通用接口和通用 class; 不是通用方法,因為它沒有任何意義:
注意:當調用 Search 時,T 將始終是繼承的抽象 class 或接口的派生 class。
我將這個解決方案加入其中; 我認為這更好,因為這意味着每個派生類型都不需要重新實現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>
{ }
}
我認為您可以顯式實現接口,以及何時通過 IMyInterface.Search 訪問方法 - 編譯器將運行正確的方法。
您需要使用顯式實現。
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>();
}
}
由於實現方式不同,這是有道理的。 如果它們沒有區別,那么基礎 class 應該實現接口,並且您應該使用協方差(僅限.Net 4.0)來組合您的約束,或者您可能根本不需要接口。
我希望我不會感到困惑,你能不能改變你的定義,比如:
public interface IMyInterface<in T>
{
//some property and method signatures
IEnumerable<U> Search<U>() where U : T, new();
}
提供T
的通用參數,可用於強制實現為T
類型提供搜索 function 約束:
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>();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.