Click here to Skip to main content
15,558,537 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
In one section of my code, I produce an array (an ArrayList to be precise) of records. Each record is derived from my BaseRecordClass i.e.
class DerivedClass1 : BaseRecordClass { ... }
class DerivedClass2 : BaseRecordClass { ... }

No records of type BaseRecordClass are ever instantiated.

Later in my code, I have:
foreach (BaseRecordClass baseRec in nameOfTheArrayList)
{
    if (baseRec is DerivedClass1)
        HandleRecord (baseRec as DerivedClass1);

    else if (baseRec is DerivedClass2)
        HandleRecord (baserec as DerivedClass2);

    ...
}

My question: is there some way to avoid the if-else if-else if construct so that I can write
foreach (BaseRecordClass baseRec in nameOfTheArrayList)
{
    HandleRecord (some magic in here to call the correct HandleRecord);
}

This example isn't bad but I've got a lot of different derived classes and the code just looks ugly with a massive if-else if-else if.

Thanks,
Judy

<edit> there's nothing wrong with this code - it works like a champ, I'm just wondering if there's a better / less-ugly way of coding it <edit>
Posted
Updated 11-Feb-11 6:31am
v2
Comments
dan!sh 11-Feb-11 11:13am    
This also means you have as many HandleRecord methods. Could you please clarify where are these HandleRecord methods? This would help suggesting better approach.
JudyL_MD 11-Feb-11 11:25am    
Yes, I have a HandleRecord for each record type. They are NOT in the derived classes, they are in the class processing the records and need to stay there because of the processing that is done. (I have this.HandleRecord in my actual code but I missed it when typing here).
dan!sh 11-Feb-11 13:01pm    
I was thinking along the lines of IOC/Dependency Injection. I think that will be too much of a redesign if you are already long way through the development process. Also, it seems that the methods are in the same class so IOC approach may not work out.

I would stick with the approach Nish has suggested.

Two options that come to mind:

1. Perhaps you can have a virtual HandleRecord in the base class and then each of the derived classes can override/implement this. Then you'd just need to call baserec.HandleRecord and the derived method will be called.

2. Use a factory class that will return an appropriate HandleRecord method (via a delegate) that you can then call. The factory would still need to do some kind of switch-case although you could also achieve that via a hash table.
 
Share this answer
 
v2
Comments
Manas Bhardwaj 11-Feb-11 11:07am    
nicely explained +5
Nish Nishant 11-Feb-11 11:10am    
Thank you.
JudyL_MD 11-Feb-11 11:28am    
Thanks

1 - see response to d@nish (functions are in caller, not the derived class)

2 - If I've got to have the "check for each possible type" I'd just rather leave it here, since this is the only place that has to do this. A factory is probably overkill for this case.
Albin Abel 11-Feb-11 13:49pm    
Hi Nishant's both options are good. But what these callers do? I hope the caller just call some properties and methods of those records. Why don't have a common interface through which only the callers get the details of the records. In that case no need to worry @ whatever derived type it is.
But if someway you must need to know the exact type to handle then factory would be better
Sergey Alexandrovich Kryukov 11-Feb-11 13:57pm    
Albin, would you please elaborate this comment into fully-fledged Answer? I thing this would be a better one!
--SA
use a abstract method or a Virtual method on the base class.

Alternatively use a Interface

abstract class BaseRecordClass : IRecordClass
   {
       public abstract void HandleRecord(BaseRecordClass records);

       /*
        Your Other Properties and Fields
        */

   }
   class DerivedClass1 : BaseRecordClass
   {
       public override void HandleRecord(BaseRecordClass records)
       {
           throw new NotImplementedException();
       }
   }

   class DerivedClass2 : BaseRecordClass
   {
       public override void HandleRecord(BaseRecordClass records)
       {
           throw new NotImplementedException();
       }
   }

   interface IRecordClass
   {
       void HandleRecord(BaseRecordClass records);
   }


If you have further questions about this please let me know.
 
Share this answer
 
I think I found a solution using Reflection. Let me explain:
1- You extract all types derived from your BaseRecord.
2- You extract all your processing methods (they take 1 argument, and this argument inherits from BaseRecords)
3- Since you have only 1 processing function per type, you make a dictionary (the key will be the type, and the value is the corresponding processing method)
4- the 'magical' function you are looking for is just getting the method out of the dictionary according to the input type.

I tried the code below and it seems to work. But even if this works, it is not very elegant. I mean, the most natural way to do the job here is to use virtual methods as everyone advises. And I would recommand that you use their suggestion.

Anyway, here is the code:

class Base { }
class DerivedClass1 : Base { }
class DerivedClass2 : Base { }
class Process
{
    public Process()
    {
        //get all classes derived from Base class
        Type baseType = typeof(Base);
        Assembly thisAssembly = Assembly.GetAssembly(baseType);
        Type[] allTypes = thisAssembly.GetTypes();
        IEnumerable<Type> baseTypes =
            from type in allTypes
            where type.IsSubclassOf(baseType)
            select type;
        //get all methods from Process class
        MethodInfo[] methods = typeof(Process).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
        //keep only the processing methods:
        //1- they have only one parameter
        //2- this parameter must inherit from Base
        List<MethodInfo> processingMethods = new List<MethodInfo>();
        foreach (MethodInfo method in methods)
        {
            ParameterInfo[] parameters = method.GetParameters();
            if (parameters.Length == 1 && parameters[0].ParameterType.IsSubclassOf(baseType))
                processingMethods.Add(method);
        }
        //now associate the type to its processing method
        Dictionary<Type, MethodInfo> dictionary = new Dictionary<Type, MethodInfo>();
        foreach (Type type in baseTypes)
        {
            foreach (MethodInfo method in processingMethods)
                if (method.GetParameters()[0].ParameterType.Equals(type))
                    dictionary[type] = method;
        }
        //lets get some records...
        Base[] baseRecords = new Base[]
        {
            new DerivedClass1(),
            new DerivedClass2(),
            new DerivedClass1()
        };
        //and test
        foreach (Base baseRec in baseRecords)
            dictionary[baseRec.GetType()].Invoke(this, new object[] { baseRec });
    }
    void ProcessDerivedClass1(DerivedClass1 param)
    {
        System.Diagnostics.Debug.Print("Processing object1...");
    }
    void ProcessDerivedClass2(DerivedClass2 param)
    {
        System.Diagnostics.Debug.Print("Processing object2...");
    }
}
class Program
{
    static void Main(string[] args)
    {
        new Process();
    }
}
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900