Use derived type in base abstract class
Asked Answered
L

4

7

Ok, I have a number of different classes deriving from a base class. This base class is an abstract containing commom methods.

One of the methods is a Copy method, wich should be present in all derived classes, so, I've put it in the base class. BUT, I want it to return the derived type not the base nor object.

The solution I got for that, is using a type paramter:

abstract class CopyableClass<T>
{
    public abstract T Copy();
}

class DerivedClass : CopyableClass<DerivedClass>
{
    public override DerivedClass Copy()
    {
        //do what is needed for copy and return a new DerivedClass
    }
}

So, the main purpose here is to

Remove the type parameter in the base class and still make the method return the corresponding derived type.


One workaround.

The best thing I could do so far is one of the comments below, but it still uses a generic parameter

abstract class BaseClass
{
    //base methods not related to deriving type
}

interface ICopyable<T>
{
     T Copy();
}

class DerivedClass : BaseClass, ICopyable<DerivedClass>
{
    public DerivedClass Copy()
    {
        //do what is needed for copy and return a new DerivedClass
    }
}
Loxodrome answered 2/7, 2013 at 17:45 Comment(8)
So what's your question?Monolayer
may be better implement this as generic interface?Taction
how to remove the type parameter in the base class and still make the method return the corresponding derived type.Above
@Daniel you should make a single generic method in the base class rather than defining it in all of the derived classes. I'll give an answer with an example method that works like this.Monolayer
That's interesting...is there a way to constrain the type parameter to be only of the deriving type?Above
@Daniel yes sir. It is in my answer.Monolayer
@Daniel yes, of cause abstract class CopyableClass<T>:where T:CopyableClass{}Taction
That clause constrains to the base type, not the deriving type. (I'm afraid I want some impossible stuff, but it would be very nice for abstract classes to use a generic deriving type)Above
E
6

You can't really. The base class can't possibly know all the future implementations. You'll have to resort to a generic abstract class (like you did) type or a generic Copy method.

public abstract class CopyableClass
{
    public abstract T Copy<T>() where T : CopyableClass;
}

public class DerivedClass : CopyableClass
{
    public override T Copy<T>()
    {
        if(typeof(T) != typeof(DerivedClass))
            throw new ArgumentException();

        // return your copy
    }
}

Or, if you want to generalize the type check in your base class:

public abstract class CopyableClass
{
    public T Copy<T>() where T : CopyableClass
    {
        if(GetType() != typeof(T))
            throw new ArgumentException();

        return (T) Copy();
    }

    protected abstract CopyableClass Copy();
}

public class DerivedClass : CopyableClass
{
    protected override CopyableClass Copy()
    {
        return // Your copy;
    }
}

Note that the second method puts alot of trust into the implementation of the derived class as it'll blindly cast the return value of the abstracted method. The compiler will let you return another type, implementing CopyableClass, in a derived type but it will be a runtime error. This is not a problem if you have the absolute control over all of the derived implementations (ie your abstract class also have an internal constructor).

Elasmobranch answered 2/7, 2013 at 17:53 Comment(0)
D
1

This will allow you to case this base class to the derived type and return it.

public abstract class BaseClass<TDerived> : where TDerived: BaseClass<TDerived>
{
   public TDerived DoSomethingCommon(string param)
   {
      var derivedType = (TElement)this;
      //do something.
      return derivedType;
   }
}
Drive answered 7/3, 2014 at 18:50 Comment(0)
Z
1

This solution involves a middle class but I think its more inline with what the you are looking for. At least you get the possible benefit of isolating your copy code

    public abstract class BaseClass
    {
    }

    public abstract class CopyableClass<T> : BaseClass
        where T: BaseClass, new()
    {
        public T Copy()
        {
            var copy = new T(); // Creating a new instance as proof of concept

            return copy;
        }
    }

    public class DerivedClass : CopyableClass<DerivedClass>
    {
    }
Zamia answered 16/11, 2017 at 21:30 Comment(0)
M
0

You actually want to implement copy in the base class and have it return T. This will make is so you call it with a type argument and it returns that type.

public static T Copy<T>() where T : CopyableClass
{
    T retVal = new T();
    // do whatever copying is required
    return retVal;
}

To call it you do;

DerivedClass d = Copy<DerivedClass>();

Your code to actually do the copy might be a bit more work to make generic but it's worth the effort given you will have a single implementation of Copy() that works for any derived type. I don't know what logic belongs in the method so I've just stubbed things out. Also, I'd recommend checking out generics in general. They're often the best option for things like this. If your implementations need to be unique to the base class' keep the same method definition but make it abstract and then override it in the base classes.

Monolayer answered 2/7, 2013 at 17:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.