简体   繁体   English

具有类型约束或基类参数的泛型方法

[英]Generic method with type constraints or base class parameter

If I write a method accepting a parameter which derives from a BaseClass (or an interface), as far as I know there are two ways to achieve that:如果我编写一个方法接受一个派生自BaseClass (或接口)的参数,据我所知,有两种方法可以实现:

void MyMethod<T>(T obj) where T : BaseClass { ... }

and

void MyMethod(BaseClass obj) { ... }

What are the differences between the two methods?这两种方法有什么区别?

In this example there isn't a big difference between the two, you can access the same members inside the method and you can call it with the same derived classes.在这个例子中,两者之间没有太大区别,您可以访问方法内部的相同成员,并且可以使用相同的派生类调用它。 There is a runtime difference as a generic method is compiled for each type it is invoked with.运行时存在差异,因为泛型方法是为调用它的每种类型编译的。

Where generics come in useful would be if you would return a value depending on T泛型有用的地方是,如果您根据T返回一个值

With generics you could do the following使用泛型,您可以执行以下操作

T MyMethod<T>(T obj) where T : BaseClass { ... }
MyMethod(derivedInstance).derivedProperty

Without this would be an error:没有这将是一个错误:

BaseClass MyMethod(BaseClass obj) { ... }
MyMethod(derivedInstance).derivedProperty // error

Note Although you mention constraining to a base class, it is worth mentioning that if you constrain not to a class, but to an interface, extra boxing will occur if the implementation is by a struct in the non generic version, this can have severe performance implications.注意虽然你提到了对基类的约束,但值得一提的是,如果你不是约束一个类,而是一个接口,如果实现是由非泛型版本中的结构体实现的,则会发生额外的装箱,这可能会产生严重的性能影响。

When T is constrained to a base class, there is not really much difference apart from what has already been stated.T被限制为基类时,除了已经说明的内容之外,实际上并没有太大区别。

When T is constrained to an interface, the difference can be huge: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

Definitely the example you quoted does not make much difference other than run time execution performance as mentioned in other answers.除了其他答案中提到的运行时执行性能之外,您引用的示例绝对没有太大区别。

Leaving aside generic collections benefits (performance improvement by avoiding boxing/unboxing for example) which we all aware of and we use frequently - Generics also works great from a consumer perspective.撇开泛型集合的好处(例如通过避免装箱/拆箱来提高性能),我们都知道并且经常使用 - 从消费者的角度来看,泛型也很有效。 For example, the below code snippet is self explanatory to visualize API usage flexibility from a consumer perspective :例如,下面的代码片段是不言自明的,可以从消费者的角度可视化 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 */
}

A couple of other use cases promoting flexibility while using generics along with constraints are :在使用泛型和约束的同时提高灵活性的其他几个用例是:

Lets say I want to ensure parameter passed to method implements IAdd and IMultiply and I have class which implements both IAdd , IMulitply like :比方说,我要确保传递给方法实现参数IAddIMultiply和我有类,它实现两个IAddIMulitply这样的:

    public class BusinessOpeartion<T> where T : IAdd, IMultiply{
    void SomeBusinessOpeartion(T obj) { /*implementation */}
}

If I need to go via non generic approach, I am forced to create redundant dummy interface like :如果我需要通过非通用方法,我将被迫创建冗余虚拟接口,如:

interface IDummy : IAdd, IMultiply

 public class BusinessOpeartion{
        void SomeBusinessOpeartion(IDummy obj) { /*implementation */}
    }

Isn't the former approach cleaner?前一种方法不是更干净吗?

Also one more small thing just popped up while typing answer.在输入答案时还弹出了一个小东西。 In case you need to, how would you get new instance for parameter type inside method:如果需要,您将如何在方法中获取参数类型的新实例:

you cannot do你不能做

IDummy dummy = new IDummy(); /*illegal*/

But with generic you could have;但是使用泛型你可以拥有; T temp = new T(); provided there is constraint of new()只要有new()约束

Also what if you need a default value for parameter type?如果您需要参数类型的默认值怎么办?

you cannot do你不能做

var default = default(IDummy); /*illegal*/

But with generic you could have;但是使用泛型你可以拥有; var default = default(T)

As was said, it matters only once you get a return value.如前所述,只有获得返回值后才重要。 Consider these cases:考虑这些情况:

BaseClass MyMethod(BaseClass)

DervivedClass temp = new DervivedClass();
//Error. My Method always returns a BaseClass. No implicit casting available
temp = MyMethod(temp);

Compare it to this:将其与此进行比较:

T MyMethod<T>(T) where T : BaseClass

DervivedClass temp = new DerivedClass();
temp = MyMethod<DerivedClass>(temp);

Strong Typification is one of the best friends you have in .NET.强类型化是您在 .NET 中最好的朋友之一。 Embrace it.拥抱它。 Never try to avoid it.永远不要试图避免它。 The opposite would be cases like we have in PHP and JavaScript: http://www.sandraandwoo.com/2015/12/24/0747-melodys-guide-to-programming-languages/相反的情况就像我们在 PHP 和 JavaScript 中的情况: http : //www.sandraandwoo.com/2015/12/24/0747-melodys-guide-to-programming-languages/

In the examples included in your question, there isn't much difference between the generic and the non-generic version.在您的问题中包含的示例中,通用版本和非通用版本之间没有太大区别。 But here are some other examples of method signatures that can't be expressed without generics:但这里有一些方法签名的其他示例,如果没有泛型就无法表达:

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 { ... }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM