[英]Overloading generic methods
當調用用於存儲對象的泛型方法時,偶爾需要以不同方式處理特定類型。 我知道你不能基於約束重載,但任何其他替代方案似乎都存在自己的問題。
public bool Save<T>(T entity) where T : class
{ ... some storage logic ... }
我喜歡做的事情如下:
public bool Save<SpecificClass>(T entity)
{ ... special logic ... }
在過去,我們的團隊已經創建了“一次性”方法來保存這些類,如下所示:
public bool SaveSpecificClass(SpecificClass sc)
{ ... special logic ... }
但是,如果您不知道該函數存在,並且您嘗試使用泛型(保存),那么您可能遇到一系列問題,即“一次性”應該修復。 如果一個新的開發人員出現,看到通用的問題,並決定他將用他自己的一次性功能來解決它,這可能會變得更糟。
所以...
解決這個看似常見的問題有哪些選擇?
我已經看過了,並且使用了UnitOfWork,現在它似乎是實際解決問題的唯一選擇 - 但似乎是用大錘攻擊蒼蠅。
你可以這樣做:
public bool Save<T>(T entity) where T : class
{ ... some storage logic ... }
public bool Save(SpecificClass entity)
{ ... special logic ... }
例如:
public class SpecificClass
{
}
public class Specializer
{
public bool GenericCalled;
public bool SpecializedCalled;
public bool Save<T>(T entity) where T : class
{
GenericCalled = true;
return true;
}
public bool Save(SpecificClass entity)
{
SpecializedCalled = true;
return true;
}
}
public class Tests
{
[Test]
public void TestSpecialization()
{
var x = new Specializer();
x.Save(new SpecificClass());
Assert.IsTrue(x.SpecializedCalled);
Assert.IsFalse(x.GenericCalled);
}
}
因為涉及泛型的函數和運算符重載在編譯時綁定而不是在運行時綁定,如果代碼有兩個方法:
public bool Save<T>(T entity) ...
public bool Save(SomeClass entity) ...
然后嘗試調用Save(Foo)
代碼,其中Foo
是某種泛型類型的變量,總是會調用前一個重載,即使泛型類型恰好是SomeClass
。 我建議解決這個問題的方法是使用非泛型方法DoSave(T param)
定義一個通用接口ISaver<in T>
。 讓提供Save
方法的類為它可以處理的類型實現所有適當的通用接口。 然后有對象的Save<T>
方法嘗試投this
一個ISaver<T>
如果演員成功,請使用生成的ISaver<T>
; 否則執行一般保存。 如果類類型聲明列出了它可以保存的類型的所有適當接口,則此方法將調用Save
調用到正確的方法。
基本上C#不允許模板特化,除非通過這樣的繼承:
interface IFoo<T> { }
class Bar { }
class FooBar : IFoo<Bar> { }
至少它在編譯期間不支持這一點。 但是,您可以使用RTTI來完成您要實現的目標:
public bool Save<T>(T entity)
{
// Check if "entity" is of type "SpecificClass"
if (entity is SpecificClass)
{
// Entity can be safely casted to "SpecificClass"
return SaveSpecificClass((SpecificClass)entity);
}
// ... other cases ...
}
該is
表現是相當方便的做運行時類型檢查。 它的工作原理類似於以下代碼:
if (entity.GetType() == typeof(SpecificClass))
// ...
編輯 :未知類型使用以下模式很常見:
if (entity is Foo)
return DoSomethingWithFoo((Foo)entity);
else if (entity is Bar)
return DoSomethingWithBar((Bar)entity);
else
throw new NotSupportedException(
String.Format("\"{0}\" is not a supported type for this method.", entity.GetType()));
編輯2 :正如其他答案建議使用SpecializedClass
重載方法,如果您正在使用多態,則需要注意。 如果您正在使用存儲庫的接口(實際上這是設計存儲庫模式的一種好方法),則有些情況下,重載將導致您調用錯誤的方法,無論您是否傳遞SpecializedClass
的對象到界面:
interface IRepository
{
bool Save<T>(T entity)
where T : class;
}
class FooRepository : IRepository
{
bool Save<T>(T entity)
{
}
bool Save(Foo entity)
{
}
}
如果您直接使用Foo
實例調用FooRepository.Save
,則此方法有效:
var repository = new FooRepository();
repository.Save(new Foo());
但是,如果您正在調用接口(例如,如果您使用模式來實現存儲庫創建),則這不起作用:
IRepository repository = GetRepository<FooRepository>();
repository.Save(new Foo()); // Attention! Call's FooRepository.Save<Foo>(Foo entity) instead of FooRepository.Save(Foo entity)!
使用RTTI只有一種Save
方法,你會沒事的。
為什么為您的方法使用不同的名稱?
請參閱以下內容:
public class Entity
{
}
public class SpecificEntity : Entity
{
}
public class Program
{
public static void Save<T>(T entity)
where T : class
{
Console.WriteLine(entity.GetType().FullName);
}
public static void Save(SpecificEntity entity)
{
Console.WriteLine(entity.GetType().FullName);
}
private static void Main(string[] args)
{
Save(new Entity()); // ConsoleApplication13.Entity
Save(new SpecificEntity()); // ConsoleApplication13.SpecificEntity
Console.ReadKey();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.