Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstract Method That Returns an Instance of Derived Class

Is it possible to create an abstract method that must return an instance of the derived class? I can do this:

abstract class Base
{
   public abstract Base GetObj();
}

class Derived : Base
{
   public Derived() { }

   public override Base GetObj()
   {
       return new Derived();
   }
}

But I was wondering if there was a way to do it such that Derived::GetObj() is forced to return a Derived?

Thanks.

like image 308
Eric Avatar asked Feb 20 '12 12:02

Eric


2 Answers

Using generics should make this possible:

abstract class Base<T>
    where T : Base<T>
{
   public abstract T GetObj();
}

class Derived : Base <Derived>
{
   public Derived() { }

   public override Derived GetObj()
   {
       return new Derived();
   }
}

You could even simplify this even more (if all of the derived instances are created with default constructors):

abstract class Base<T>
    where T : Base<T>, new()
{
    public static T GetObj()
    {
        return new T();
    }
}

class Derived : Base<Derived>
{
    public Derived() { }
}
like image 116
Lukazoid Avatar answered Oct 23 '22 19:10

Lukazoid


What you have is almost but not quite exactly an abstract factory. I will first say that you should leave it up to the implementers of the derived classes to get it right, or simply trust that they will.

Another answer has shown what is known as the curiously recurring template pattern. It is where you have a base class that tries to use the type system to enforce that derived types use itself at certain input or output positions.

public abstract class Foo<T> where T : Foo<T>
public class Bar : Foo<Bar>

This idea might work in other languages. It works in C# only so far as people use it correctly. With the above definition of Bar, now I can also have

public class Baz : Foo<Bar> 

Which is perfectly legal. Bar is a Foo<Bar>, which is all that is required for Baz to use it. Nothing requires Baz to actually use Foo<Baz>.

The type system in C# simply cannot enforce what you would like enforced. Even with this pattern in place, you are still in the same position as before. You still have to trust the implementers of the derived classes to do it correctly.

For more on this topic, you might read this blog.

like image 26
Anthony Pegram Avatar answered Oct 23 '22 19:10

Anthony Pegram