1

I'm trying to refactor a method consisting of multiple if-statements with different conditions. The method looks like this:

private void DeserializeProperty(object value, 
                                 PropertyInfo property, Format format) {
     if (value == DBNull.Value || value == null) {
        property.SetValue(this, null);
        return;
     }

     if (property.PropertyType == typeof(Id)) {
        SetIdProperty(value, property);
        return;
     }

     if (property.PropertyType.BaseType == typeof(BaseProperty)) {
        SetBaseProperty(value, property);
        return;
     }

     if (property.PropertyType == typeof(Data)) {
        DeserializeData(value, property, format);
        return;
     }

     DeserializeNormalProperty(property, value);
}

Replacing these If-statements with polymorphism won't work afaik (not sure if it would be wise to do it if it would work) since the condition is concerning the PropertyType

Replacing them with a Dictionary of type Dictionery<Type,Action<object, PropertyInfo>> won't work since the method DeserializeData(value, property, format); would not fit in the action

Additionally non of the Solutions above would handle the value == DBNull.Value || value == null Condition

How do I solve this?

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Visions
  • 919
  • 1
  • 7
  • 17
  • Why are you using reflection? What does this method does? Post small but complete sample program. Also, if your code works fine it is a better fit for [codereview](http://codereview.stackexchange.com/) – Sriram Sakthivel Sep 10 '14 at 12:54
  • I'm using reflection since this method is within a superclass, this way it works for the rest of the classes inheriting from this one. – Visions Sep 10 '14 at 12:56
  • No, I still don't understand why do you need reflection. Post a complete sample(which should compile and work). It may get you a better answer. – Sriram Sakthivel Sep 10 '14 at 12:57
  • Your method looks fine to me, I'm not sure it needs much refactoring. The only point of concern is, you pass an input variable `format`, which is only used a portion of the time. – Eren Ersönmez Sep 10 '14 at 13:02

1 Answers1

1

Just a quick warning - neither of the below are refactors as such, just ways of rewriting what you already have. Given the variable nature of the conditions and their imposed ordering (eg basetype of baseproperty taking precedence over data), it is hard to think of anything better or clearer than an if-else. IMO you can't really improve on what you have by just refactoring that one function. But I'll present some possible re-writes anyway in case either appeals.

Could use elses (simply for readability):

Note: Under some coding guidelines, this is less preferential to what you already have. Personally, I find it more readable however.

private void DeserializeProperty(object value, 
                                 PropertyInfo property, Format format) {
    if (value == DBNull.Value || value == null) {
         property.SetValue(this, null);
    }
    else if (property.PropertyType == typeof(Id)) {
         SetIdProperty(value, property);
    }
    else if (property.PropertyType.BaseType == typeof(BaseProperty)) {
         SetBaseProperty(value, property);
    }
    else if (property.PropertyType == typeof(Data)) {
         DeserializeData(value, property, format);
    }
    else {
         DeserializeNormalProperty(property, value);
    }
 }

An Alternative (if you so desire)

If you are doing lots of similar switches, using a custom CleverSwitch class similar to this:

https://stackoverflow.com/a/299120/3940783

might work for you. It'd be rather similar to your idea about the dictionary of Actions - but due to the variation in your conditions, it'd be cleanest to have Actions with no inputs and just use the variables from the DeserializeProperty's scope without passing them into the action.

So, for example, you could replace the function with the following:

private void DeserializeProperty(object value, 
                                 PropertyInfo property, Format format) {
    CleverSwitch.Do(
        CleverSwitch.If(() => value == DBNull.Value || value == null, () => property.SetValue(this, null))
        CleverSwitch.IsType<Id>(property.PropertyType, () => SetIdProperty(value, property)),
        CleverSwitch.IsType<BaseProperty>(property.PropertyType.BaseType, () => SetBaseProperty(value, property)),
        CleverSwitch.IsType<Data>(property.PropertyType, () => DeserializeData(value, property, format)),
        CleverSwitch.Default(() => DeserializeNormalProperty(property,value))
    );
}

Where CleverSwitch works similarly to JaredPar's TypeSwitch at the above link - and could be coded as follows:

static class CleverSwitch {
    public class CaseInfo {
        public Func<bool> Condition { get; set; }
        public Action Action { get; set; }
    }

    public static void Do(object source, params CaseInfo[] cases) {
        var type = source.GetType();
        foreach (var entry in cases) {
            if (entry.Condition()) {
                entry.Action();
                break;
            }
        }
    }

    public static CaseInfo IsType<T>(Type T2, Action action) {
        return new CaseInfo() { Condition = () => T2 == typeof(T), Action = action };
    }

    public static CaseInfo If(Func<bool> condition, Action action) {
        return new CaseInfo() { Condition = condition, Action = action };
    }

    public static CaseInfo Default(Action action) {
        return new CaseInfo() { Condition = () => true, Action = action };
    }
}

But on balance:

Any class to help you refactor it in the manner of the dictionary approach is going to have to be rather general in this case, and so essentially just be confusing and add performance overheads. So whilst the above code should work, I really can't see how it improves on an if statement in any of the following areas:

  • time to write
  • readability or
  • maintainability

In fact, I'd argue that a simple if, return or if, else combination is more readable, maintainable and easier to write straight off... But that's just my two cents!

Community
  • 1
  • 1
David E
  • 1,384
  • 9
  • 14
  • What I would say though is if `DeserializeNormalProperty` is under your control, refactoring it to take its arguments the other way around (ie, `DeserializeNormalProperty(value, property);`) would fit in with the others much better for consistency. (In fact, I'd say maybe `property, value` makes more sense, so maybe it's the other methods which should have their parameter order swapped!) – David E Sep 10 '14 at 14:30