Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

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

and

void MyMethod(BaseClass obj) { ... }

What are the differences between the two methods?

like image 731
Thomas Flinkow Avatar asked Feb 12 '18 14:02

Thomas Flinkow


People also ask

What is a generic type parameter?

Generic Methods A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.

What is the purpose of the class constraint on a type parameter?

Object, you'll apply constraints to the type parameter. For example, the base class constraint tells the compiler that only objects of this type or derived from this type will be used as type arguments. Once the compiler has this guarantee, it can allow methods of that type to be called in the generic class.

How do you indicate that a class has a generic type parameter?

A generic type is declared by specifying a type parameter in an angle brackets after a type name, e.g. TypeName<T> where T is a type parameter.

Can generic classes be constrained?

You can constrain the generic type by interface, thereby allowing only classes that implement that interface or classes that inherit from classes that implement the interface as the type parameter. The code below constrains a class to an interface.

How to use type parameter as a type constraint?

Type parameters can also be used as constraints in generic class definitions. The type parameter must be declared within the angle brackets together with any other type parameters: C#. //Type parameter V is used as a type constraint. public class SampleClass<T, U, V> where T : V { }

What is a base class constraint in Java?

The base class constraint states that a type to be used as a type argument for that generic type has the specified class as a base class, or is that base class. If the base class constraint is used, it must appear before any other constraints on that type parameter. Some types are disallowed as a base class constraint: Object, Array, and ValueType.

What is base class constraint in typescript?

The base class constraint states that a type to be used as a type argument for that generic type has the specified class as a base class, or is that base class. If the base class constraint is used, it must appear before any other constraints on that type parameter.

Why can't we have a generic parameter inside a class?

Inside the class, they would have to be treated like object if they were allowed to be of completely different types. That means you could just as well leave out the generic parameter and provide overloads. @Mansfield, it's because an or relationship makes things far to general to be useful.


3 Answers

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

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.

like image 132
Titian Cernicova-Dragomir Avatar answered Oct 13 '22 15:10

Titian Cernicova-Dragomir


When T is constrained to a base class, there is not really much difference apart from what has already been stated.

When T is constrained to an interface, the difference can be huge:

int FrobNonGeneric(IFrobbable frob) { //... }
int Frob<T>(T frob) where T: IFrobbable { //... }

struct Frob: IFrobbable { ... }

FrobNonGeneric(new Frob()); //boxing!
Frob(new Frob()); //no boxing
like image 10
InBetween Avatar answered Oct 13 '22 15:10

InBetween


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 :

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 :

    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()

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)

like image 5
rahulaga_dev Avatar answered Oct 13 '22 15:10

rahulaga_dev