Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloading generic methods

Tags:

When calling a generic method for storing an object there are occasionally needs to handle a specific type differently. I know that you can't overload based on constraints, but any other alternative seems to present its own problems.

public bool Save<T>(T entity) where T : class { ... some storage logic ... } 

What I would LIKE to do is something like the following:

public bool Save<SpecificClass>(T entity) { ... special logic ... } 

In the past our team has created 'one-off' methods for saving these classes as follows:

public bool SaveSpecificClass(SpecificClass sc) { ... special logic ... } 

However, if you don't KNOW that function exists, and you try to use the generic (Save) then you may run into a host of problems that the 'one-off' was supposed to fix. This can be made worse if a new developer comes along, sees the problem with the generic, and decides he's going to fix it with his own one-off function.

So...

What are the options for working around this seemingly common issue?

I've looked at, and used UnitOfWork and right now that seems to be the only option that actually resolves the problem - but seems like attacking a fly with a sledgehammer.

like image 753
Patrick Dench Avatar asked Mar 20 '13 13:03

Patrick Dench


People also ask

How can generic methods be overloaded develop a code with example?

A generic method can also be overloaded by non-generic methods that have the same method name and number of parameters. When the compiler encounters a method call, it searches for the method declaration that most precisely matches the method name and the argument types specified in the call.

What are overloading methods?

Overloading happens when you have two methods with the same name but different signatures (or arguments). In a class we can implement two or more methods with the same name. Overloaded methods are differentiated based on the number and type of parameter passed as arguments to the methods.

Can we override generic method in C#?

Similar to John Carpenter's answer, you can override the generic method with the same generic method, but simply use the as operator to check and cast it to the desired type. This has the added benefit of using null-testing to check if the conversion worked.

What are generic methods?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.


2 Answers

You could do :

public bool Save<T>(T entity) where T : class { ... some storage logic ... }  public bool Save(SpecificClass entity) { ... special logic ... } 

For example:

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);     } } 
like image 117
Peter Ritchie Avatar answered Oct 14 '22 11:10

Peter Ritchie


Well basicly C# does not allow template specialization, except through inheritence like this:

interface IFoo<T> { } class Bar { }  class FooBar : IFoo<Bar> { } 

At least it does not support this during compile time. However you can use RTTI to do what you are trying to achieve:

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

The is expression is pretty handy to do runtime type checks. It works similar to the following code:

if (entity.GetType() == typeof(SpecificClass))     // ... 

EDIT : It is pretty common for unknown types to use the following pattern:

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

EDIT 2 : As the other answers suggest overloading the method with the SpecializedClass you need to take care if you are working with polymorphism. If you are using interfaces for your repository (which is actually a good way to design the repository pattern) there are cases where overloading would lead to cases in which you are the wrong method get's called, no matter if you are passing an object of SpecializedClass to the interface:

interface IRepository {     bool Save<T>(T entity)         where T : class; }  class FooRepository : IRepository {     bool Save<T>(T entity)     {     }      bool Save(Foo entity)     {     } } 

This works if you directly call FooRepository.Save with an instance of Foo:

var repository = new FooRepository(); repository.Save(new Foo()); 

But this does not work if you are calling the interface (e.g. if you are using patterns to implement repository creation):

IRepository repository = GetRepository<FooRepository>(); repository.Save(new Foo());  // Attention! Call's FooRepository.Save<Foo>(Foo entity) instead of FooRepository.Save(Foo entity)! 

Using RTTI there's only one Save method and you'll be fine.

like image 43
Carsten Avatar answered Oct 14 '22 13:10

Carsten