当调用存储对象的通用方法时,偶尔需要以不同方式处理特定类型。我知道你不能基于约束重载,但任何其他替代方案似乎都存在问题。
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,它似乎只是 选项实际上解决了这个问题 - 但似乎是用大锤攻击苍蝇。
答案 0 :(得分:16)
你可以这样做:
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);
}
}
答案 1 :(得分:3)
因为涉及泛型的函数和运算符重载在编译时而不是运行时绑定,如果代码有两种方法:
public bool Save<T>(T entity) ...
public bool Save(SomeClass entity) ...
然后尝试调用Save(Foo)
的代码,其中Foo
是某个泛型类型的变量将始终调用前一个重载,即使泛型类型恰好是SomeClass
。我建议解决这个问题的方法是使用非泛型方法ISaver<in T>
定义通用接口DoSave(T param)
。让提供Save
方法的类为它可以处理的类型实现所有适当的通用接口。然后让对象的Save<T>
方法尝试将this
投射到ISaver<T>
。如果演员成功,请使用结果ISaver<T>
;否则执行一般保存。如果类类型声明列出了它可以保存的类型的所有适当接口,则此方法将调用Save
调用正确的方法。
答案 2 :(得分:2)
基本上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
expression非常方便进行运行时类型检查。它的工作方式类似于以下代码:
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
的方法,如果您正在使用多态,则需要注意。如果您正在使用存储库的接口(这实际上是设计存储库模式的好方法),则有些情况下,重载会导致您调用错误的方法,无论您是否传递{的对象{1}}到界面:
SpecializedClass
如果您直接使用interface IRepository
{
bool Save<T>(T entity)
where T : class;
}
class FooRepository : IRepository
{
bool Save<T>(T entity)
{
}
bool Save(Foo entity)
{
}
}
FooRepository.Save
,则此方法有效
Foo
但如果您正在调用接口(例如,如果您使用模式来实现存储库创建),则这不起作用:
var repository = new FooRepository();
repository.Save(new Foo());
使用RTTI只有一种IRepository repository = GetRepository<FooRepository>();
repository.Save(new Foo()); // Attention! Call's FooRepository.Save<Foo>(Foo entity) instead of FooRepository.Save(Foo entity)!
方法,你会没事的。
答案 3 :(得分:0)
为什么为您的方法使用不同的名称?
请参阅以下内容:
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();
}
}