如何重构普遍的if语句进行类型检查

时间:2016-03-28 09:41:09

标签: c# generics refactoring type-conversion

我正在使用已有大约十年的遗留系统,其中一个基本数据结构定义如下:

[Serializable()]
public class DataClass
{
    private Array _values;
    private readonly Type _valueType;

    public DataClass(Array tmpArray, Type tmpType)
    {
        _values = tmpArray;
        _valueType = tmpType;
    }

    public Array GetValues()
    {
        return _values;
    }

    public Type ValueType
    {
        get { return _valueType; }
    }

    public void SetValues(Array newValues, int fromIndex)
    {
        // 1. type check, if _values and newValues don't share same data type, throws an exception

        // 2. length check
        if (fromIndex + newValues >= _values.Length)
           throws new InvalidDataException();

        // 3. set values
        for (var i = fromIndex; i < newValues.Length; i++)
             _values.SetValue(newValues.GetValue(i - fromIndex), i);  
    }

    ...blahblah
}

我认为主动是他们想要仅使用一个类来支持不同的数据类型,例如

new DataClass(new int[]{1,2,3,4}, typeof(int));

new DataClass(new float[]{1f,2f,3f,4f}, typeof(float));

现在我想使用默认值初始化DataClass,在分析后我发现API SetValues对于较长的数组(boxingunboxing而言相当慢)并且使程序响应性降低,我决定使用 Generic 和许多 if else 语句来加速,例如:

void InitValues(DataClass data)
{
    if (data.ValueType == typeof(int))
        InitWith(data, -1);
    else
    if (data.ValueType == typeof(double))
        InitWith(data, -9.99d);

    ...blahblah

}

void InitWith<T>(DataClass data, T defaultValue)
{
    // much faster
    var array = (T[])data.GetValues();
    for (var i = 0; i < array.Length; i++)
         array[i] = defaultValue;
}     

然而,我有很多性能关键方法,如InitValues。由于DataClass支持的值类型太多,编写和维护这些代码会很烦人。

鉴于我不拥有DataClass的源代码,我无法对DataClass进行任何更改。我想知道是否有一种方法可以重构,以便我可以在一个地方中处理所有类型检查的if语句?

1 个答案:

答案 0 :(得分:2)

鉴于您不允许更改DataClass,我们需要让DataClass的消费者高效。一种方法是使用词典将类型映射到动作/方法。我们只需要初始化这些词典一次。这是这样一个类的一个例子。

class ConsumerClass // the one which uses DataClass objects
{
    // All the Mappings required by this consumer class
    readonly Dictionary<Type, Action<DataClass>> InitMap = new Dictionary<Type, Action<DataClass>>();
    readonly Dictionary<Type, Action<DataClass>> DoSomethingAMap = new Dictionary<Type, Action<DataClass>>();
    readonly Dictionary<Type, Action<DataClass>> DoSomethingBMap = new Dictionary<Type, Action<DataClass>>();

    // Constructor
    public ConsumerClass()
    {
        // Initialize all the mappings for all the required types for this consumer class here. 
        // This is a one time overhead, but will definitely speedup the methods within this class
        // You could move this part further up the hierarchy of inheritance, to avoid repetitions in every other consumer class.

        // For int
        InitMap.Add(typeof(int), data => InitWith(data, -1));
        DoSomethingAMap.Add(typeof(int), DoSomethingA<int>);
        DoSomethingBMap.Add(typeof(int), DoSomethingB<int>);

        // For double
        InitMap.Add(typeof(double), data => InitWith(data, -9.99d));
        DoSomethingAMap.Add(typeof(double), DoSomethingA<double>);
        DoSomethingBMap.Add(typeof(double), DoSomethingB<double>);

        // other types, if needed by this consumer
    }

    void InitValues(DataClass data)
    {
        // This takes care of your if s
        InitMap[data.ValueType].Invoke(data);
    }
    void InitWith<T>(DataClass data, T defaultValue)
    {
        // much faster
        var array = (T[])data.GetValues();
        for (var i = 0; i < array.Length; i++)
            array[i] = defaultValue;
    }

    void DoSomethingA(DataClass data)
    {
        DoSomethingAMap[data.ValueType].Invoke(data);
    }
    void DoSomethingA<T>(DataClass data)
    {
        var array = (T[])data.GetValues();
        // do something
    }

    void DoSomethingB(DataClass data)
    {
        DoSomethingBMap[data.ValueType].Invoke(data);
    }
    void DoSomethingB<T>(DataClass data)
    {
        var array = (T[])data.GetValues();
        // do something
    }
}

构造函数中有一些冗余代码,因此仍然可以有更好的方法来编写此机制。但是你应该知道如何清理你的ifs并仍然提高性能。