如果我编写一个方法来接受一个派生自BaseClass
(或接口)的参数,据我所知有两种方法可以实现:
void MyMethod<T>(T obj) where T : BaseClass { ... }
和
void MyMethod(BaseClass obj) { ... }
使用一个优于另一个的优点/缺点是什么?
答案 0 :(得分:17)
在这个示例中,两者之间没有很大的区别,您可以访问方法内的相同成员,并且可以使用相同的派生类调用它。由于为其调用的每种类型编译了泛型方法,因此存在运行时差异。
如果您根据T
使用泛型,您可以执行以下操作
T MyMethod<T>(T obj) where T : BaseClass { ... }
MyMethod(derivedInstance).derivedProperty
如果没有这将是一个错误:
BaseClass MyMethod(BaseClass obj) { ... }
MyMethod(derivedInstance).derivedProperty // error
注意虽然您提到了对基类的约束,但值得一提的是,如果不限制类,而是限制接口,如果实现是通过结构实现的,则会发生额外的限制非通用版本,这可能会产生严重的性能影响。
答案 1 :(得分:8)
当T
被约束为基类时,除了已经陈述的内容之外,没有太大区别。
当T
被约束到接口时,差异可能很大:
int FrobNonGeneric(IFrobbable frob) { //... }
int Frob<T>(T frob) where T: IFrobbable { //... }
struct Frob: IFrobbable { ... }
FrobNonGeneric(new Frob()); //boxing!
Frob(new Frob()); //no boxing
答案 2 :(得分:5)
除了其他答案中提到的运行时执行性能之外,您所引用的示例绝对没有太大区别。
不考虑通用集合的好处(例如通过避免装箱/拆箱来提高性能)我们都知道并经常使用 - 从消费者的角度来看,泛型也很有用。例如,以下代码片段可以自我解释,从消费者的角度可视化API使用灵活性:
interface IEntity
{
int Id {get;set;}
}
class Student : IEntity
{
int Id {get;set;}
string SubjectOpted {get;set;}
}
class Employee : IEntity
{
int Id {get;set;}
string DepartmentName{get;set;}
}
interface INonGenericRepository
{
IEntity Get(int id)
}
interface IGenericRepository<T> where T:Entity
{
T Get(int id)
}
class NonGenericRepository : IRepository
{
public IEntity Get(int id) {/*implementation goes here */
}
class GenericRepository<T> : IRepository<T>
{
public T Get(int id) {/*implementation goes here */
}
Class NonGenericStudentConsumer
{
IEntity student = new NonGenericFRepository().Get(5);
var Id = student.Id
var subject = student.SubjectOpted /*does not work, you need to cast */
}
Class GenericStudentConsumer
{
var student = new GenericFRepository<Student>().Get(5);
var Id = student.Id
var subject = student.SubjectOpted /*works perfect and clean */
}
在使用泛型和约束时提高灵活性的其他一些用例包括:
假设我想确保传递给方法实现IAdd
和IMultiply
的参数,并且我有一个实现IAdd
,IMulitply
的类,如:
public class BusinessOpeartion<T> where T : IAdd, IMultiply{
void SomeBusinessOpeartion(T obj) { /*implementation */}
}
如果我需要通过非通用方法,我被迫创建冗余虚拟接口,如:
interface IDummy : IAdd, IMultiply
public class BusinessOpeartion{
void SomeBusinessOpeartion(IDummy obj) { /*implementation */}
}
前一种方法不是更清洁吗?
在输入答案时,还会弹出一个小东西。如果您需要,如何在方法内获取参数类型的新实例:
你做不到
IDummy dummy = new IDummy(); /*illegal*/
但是你可以使用通用的; T temp = new T();
提供了new()
如果您需要参数类型的默认值,该怎么办?
你做不到
var default = default(IDummy); /*illegal*/
但是你可以使用通用的; var default = default(T)
答案 3 :(得分:3)
正如所说,只有获得回报价值才有意义。考虑以下情况:
BaseClass MyMethod(BaseClass)
DervivedClass temp = new DervivedClass();
//Error. My Method always returns a BaseClass. No implicit casting available
temp = MyMethod(temp);
将其与此相比:
T MyMethod<T>(T) where T : BaseClass
DervivedClass temp = new DerivedClass();
temp = MyMethod<DerivedClass>(temp);
Strong Typification是您在.NET中最好的朋友之一。接受它。永远不要试图避免它。相反的情况就像我们在PHP和JavaScript中的情况:http://www.sandraandwoo.com/2015/12/24/0747-melodys-guide-to-programming-languages/
答案 4 :(得分:1)
在您的问题中包含的示例中,通用版和非通用版之间没有太大区别。但是这里有一些方法签名的例子,它们不能用泛型表达:
T MyMethod<T>(T obj) where T : BaseClass { ... }
void MyMethod<T>(T obj1, T obj2) where T : BaseClass { ... }
void MyMethod<T>(T obj, List<T> list) where T : BaseClass { ... }