C# Generic Base Polymorphism

Question

i have one generic base class and two classes derived from base class. I want to create this kind of derived classes with a object factory. But i can not do this with a generic base class. I can do this, this way, in Java without any problems. I also solved this in c# with boxing and unboxing. What is the proper way to do this ?

public abstract class Base<T> {
    public abstract T Value {
        get;
        set;
    }
}

public class Derived1 : Base<int> {
    int value;

    public override int Value {
        get {
            return value;
        }
        set {
            this.value = value;
        }
    }
}

public class Derived2 : Base<float> {
    float value;

    public override float Value {
        get {
            return value;
        }
        set {
            this.value = value;
        }
    }
}

public class Factory {
    public Base create(int type) {
        switch(type) {
            case 1:
                return new Derived1();
            case 2:
                return new Derived2();
        }
    }
}

Thank you!

Solved : I understand that since any interface won't have the generic method they also won't help. Only solution is boxing the value into an object. Here is my solution.

public abstract class Base {
    public abstract object Value {
        get;
        set;
    }
}

public class Derived1 : Base {
    int value;

    public override object Value {
        get {
            return value;
        }
        set {
            this.value = (int) value;
        }
    }
}

public class Derived2 : Base {
    float value;

    public override object Value {
        get {
            return value;
        }
        set {
            this.value = (float) value;
        }
    }
}

public class Factory {
    public Base create(int type) {
        switch(type) {
            case 1:
                return new Derived1();
            case 2:
                return new Derived2();
            default:
                return null;
        }
    }
}

Now i can treat all derived objects as Base object.


Show source
| generics   | c#   | inheritance   | polymorphism   2016-12-30 00:12 2 Answers

Answers ( 2 )

  1. 2016-12-30 00:12

    You will have to have a non-generic Base<T> return type; You might consider using TypeCode:

    public class Factory {
        public Base<T> create<T>() {
            var typeCode = Type.GetTypeCode(typeof(T));
            switch (typeCode)
            {
                case TypeCode.Empty:
                    break;
                case TypeCode.Object:
                    break;
                case TypeCode.DBNull:
                    break;
                case TypeCode.Boolean:
                    break;
                case TypeCode.Char:
                    break;
                case TypeCode.SByte:
                    break;
                case TypeCode.Byte:
                    break;
                case TypeCode.Int16:
                    break;
                case TypeCode.UInt16:
                    break;
                case TypeCode.Int32:  // Derived1 : Base<int>
                    return new Derived1();
    
                    break;
                case TypeCode.UInt32:
                    break;
                case TypeCode.Int64:
                    break;
                case TypeCode.UInt64:
                    break;
                case TypeCode.Single:  // Derived2 : Base<float>
                    return new Derived2();
    
                    break;
                case TypeCode.Double:
                    break;
                case TypeCode.Decimal:
                    break;
                case TypeCode.DateTime:
                    break;
                case TypeCode.String:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
    

    ...also, if you used an interface you can "hide" the <TypeParam> from the return:

    public interface IBase {
        // properties/methods
    }
    

    Then:

    public abstract class Base<T> : IBase {
            // properties/methods
    }
    

    And then:

    public class Factory {
        public IBase create<T>() {
            // logic
        }
    }
    

    Now, for returning Value there are three options: (1) stick with implementation without the interface IBase; (2) use dynamic return type in the interface, or; (3) box the values in object as you have done.

    NOTE: writing off the top of my head, untested.

    // 1. implementation without the interface    
    public abstract class Base<T> {
        public abstract T Value {get; set;}
    }
    
    // 2. implementation with IBase
    public interface IBase {
        public dynamic Value {get; set;}
    }
    
    public abstract Base<T> : IBase {
        public abstract dynamic Value {get; set;}
    }
    
  2. 2016-12-30 00:12

    Here is one way of achieving what you want but the caller of the factory will need to be aware of the type being created. I also changed the properties in your derived classes to automatic properties since you were not doing anything special with them but you can change them back if you wish.

    public abstract class Base<T> {
       public abstract T Value
       {
          get;
          set;
       }
    }
    
    public class Derived1 : Base<int> {
       public override int Value { get; set; }
    }
    
    public class Derived2 : Base<float> {
       public override float Value { get; set; }
    }
    
    public class Factory {
       public Base<T> Create<T>(int type) {
          switch( type ) {
             case 1:
             return new Derived1() as Base<T>;
             case 2:
             return new Derived2() as Base<T>;
          }
    
          return null; // or whatever you want to do
       }
    }
    

    Here is the usage:

    var f = new Factory();
    var one = f.Create<int>( 1 );
    var two = f.Create<float>( 2 );
    

    Like I mentioned the caller of the factory needs some knowledge of the types being created because this will fail:

    var f = new Factory();
    var one = f.Create<int>( 2 );   // <-------- Changed from 1 to 2
    var two = f.Create<float>( 1 ); // <-------- Changed from 2 to 1
    

    You can always throw an exception from the factory to explain why you could not create the type requested so callers can then take the appropriate steps.

◀ Go back