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

How to refactor the pervasive if statement for type check

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

[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
}

我认为他们希望仅使用一种 class 来支持不同的数据类型,例如

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 进行任何更改。想知道有没有办法重构,让我在一个地方?

处理所有类型check的if语句

鉴于您不能更改 DataClass,我们需要提高 DataClass 的使用者的效率。一种方法是使用字典将类型映射到 Actions/Methods。我们只需要初始化这些字典一次。这是 class.

的示例
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 并仍然提高性能。