Nico de Wit
Nico de Wit

Reputation: 73

Create class based up on abstract type

I have selveral classes based upon a standard abstract class and they will be loaded from a datatable (for the sample I used an array of int). The class wil be intiated by type and then load the specific details for that class. Currenlty I am doing this with a switch statement but is it possbile to do this an other way?

class Program
{
    static void Main(string[] args)
    {
        var list = new[] {1, 1, 3, 2, 2, 4};
        TypeBase typeClass = null;
        foreach (var i in list)
        {
            switch (i)
            {
                case 1:
                    {
                        typeClass = new Type1();
                        break;
                    }
                case 2:
                    {
                        typeClass = new Type2();
                        break;
                    }
                case 3:
                    {
                        typeClass = new Type3();
                        break;
                    }
            }
        }
        if (typeClass != null)
        {
            typeClass.LoadDetails();
        }
    }
}

public class TypeBase
{
    public int Type { get; set; }
    public virtual void LoadDetails()
    {
        throw new NotImplementedException();
    }
}

public class Type1 : TypeBase
{
    public override void LoadDetails()
    {
        // Load type Specific details
    }
}

public class Type2 : TypeBase
{
    public override void LoadDetails()
    {
        // Load type Specific details
    }
}

public class Type3 : TypeBase
{
    public override void LoadDetails()
    {
        // Load type Specific details
    }
}

Upvotes: 2

Views: 140

Answers (2)

Martin Mulder
Martin Mulder

Reputation: 12944

There are several solutions.

1. Create types with reflection

This solution has already been given by Nair. Since I expect your types will not be called Type1, Type2, etc. I assume this will not work for you.

2. Store types in a dictionaries with types

Create a dictionary which will replace your switch/case. It contains the types you need to create:

Dictionary<int, Type> types = new Dictionary<int, Type>
{
    {1, typeof(Type1)},
    {2, typeof(Type2)},
    {3, typeof(Type3)}
};

use this to create your types:

Type type;
if (types.TryGetValue(i, out type))
{
    TypeBase typeClass = (TypeBase)Activator.CreateInstance(type);
    typeClass.LoadDetails();
}

This solution is faster than solution #1, since only one "reflection" operation is used.

3. Store types in a dictionaries with factory methods

Create a dictionary which will replace your switch/case. It will contain factory methods:

Dictionary<int, Func<TypeBase>> factories = new Dictionary<int, Func<TypeBase>>
{
    {1, () => new Type1()},
    {2, () => new Type2()},
    {3, () => new Type3()}
};

use this to create your types:

Func<TypeBase> factory;
if (factories.TryGetValue(i, out factory))
{
    TypeBase typeClass = factory();
    typeClass.LoadDetails();
}

This solution is faster than solution #2, since there is not reflection used.

4. Store types in a dictionaries with instances

Create a dictionary which will replace your switch/case. It will contain instances of your types. This solution will only work if these instances are immutable and will not change state during calls.

Dictionary<int, TypeBase> typeClasses = new Dictionary<int, TypeBase>
{
    {1, new Type1()},
    {2, new Type2()},
    {3, new Type3()}
};

use this to create your types:

TypeBase typeClass;
if (baseClasses.TryGetValue(i, out baseClass))
{
    typeClass.LoadDetails();  
}

This solution is faster than solution #3, since no instances are created with every call.

Some side notes

  • Why not use an interface, with one member LoadDetails? In your example member Type is never used.
  • Why not make TypeBase.LoadDetails an abstract method?
  • If your keys are always of type Int32 and are in a continuous range, you could consider using a List<T> or even an array, which will be faster than a dictionary.

Upvotes: 3

S.N
S.N

Reputation: 5140

Ideally, you should follow factory method to such requirement. Else, if you are happy to follow conventions for your subclass then a cheap trick would be something like below.

using System;
using System.Runtime.Remoting;

namespace ConsoleApplication1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var list = new[] {1, 1, 3, 2, 2, 4};
            TypeBase typeClass = null;
            foreach (var i in list)
            {
                ObjectHandle handle = Activator.CreateInstanceFrom("ConsoleApplication1", string.Format("{0}{1}", "Type", i));//Program- Name of the assembl
                var typeBase = (TypeBase) handle.Unwrap();
                typeBase.Type = i;
                typeClass.LoadDetails();
            }
        }
    }

    public class TypeBase
    {
        public int Type { get; set; }

        public virtual void LoadDetails()
        {
            throw new NotImplementedException();
        }
    }

    public class Type1 : TypeBase
    {
        public override void LoadDetails()
        {
            // Load type Specific details
        }
    }
}

Note : I personally won't follow such approach and would more pleased with factory methods or fair number of switches. This is just to flash a possibility only. Please test and amend accordingly (if decide to follow)

Upvotes: 1

Related Questions