Primitive type conversion in generic method without boxing

时间:2017-08-04 12:53:57

标签: c# boxing

While doing some profiling on one of our applications, I found this code:

public TOut GetValue<TIn, TOut>(Func<TIn> getter)
{
    var value = getter();

    // Do some stuff with the value

    return (TOut)Convert.ChangeType(value, typeof(TOut));
}

TIn and TOut are either int, double, or string.

This showed up in the profiling sessions as an important source of heap allocations because of the boxing when using int or double. The input value of Convert.ChangeType is boxed because the method expects an object, and the return value is boxed for the same reason.

I'm trying to optimize this code because this method is used in a high-throughput service, where this kind of allocation is prohibitive. Ideally, I would have rewritten the method as non-generic, but the API is widely used by various teams and a refactoring of such scale will take months. In the meantime I'm trying to mitigate the issue and find a way to improve the situation without changing the API contract. However I've been struggling on this for a while and have yet to find a solution.

Do you know a way, even ugly, to handle the int -> double and double -> int conversion without boxing, given the method contract? Note that I can't change the parameters but I could add a generic constraint (such as where TIn : IConvertible, but this hasn't helped me much).

1 个答案:

答案 0 :(得分:1)

如果您只需要专门进行几次转换,我认为以下方法有效并且不会产生任何每次通话分配:

    private static int FromDouble(double other)
    {
        return (int)other;
    }
    private static double FromInt(int other)
    {
        return (double)other;
    }

    private static Func<double, int> di = FromDouble;
    private static Func<int, double> id = FromInt;
    public TOut GetValue<TIn, TOut>(Func<TIn> getter)
    {
        var value = getter();

        // Do some stuff with the value

        if (typeof(TIn) == typeof(int) && typeof(TOut) == typeof(double))
        {
            var gf = (Func<TIn, TOut>)(object)id;
            return gf(value);
        }else if (typeof(TIn) == typeof(double) && typeof(TOut) == typeof(int))
        {
            var gf = (Func<TIn, TOut>)(object)di;
            return gf(value);
        }

        return (TOut)Convert.ChangeType(value, typeof(TOut));
    }

当然,还有一些进一步的调整可以做到这一点。