如何避免值类型的装箱

How to avoid boxing of value types

假设我有一些 class 根据传递给它的通用类型调用 Func,并且我有一个所需的 public 接口用于 class 东西像这样:

var r = new Resolver(); 
var i = r.Invoke(10); // invokes some function `Func<int,int>`
var j = r.Invoke("Hello"); // Same, but `Func<string,string>`
var k = r.Invoke(10, 10); // Same but 'Func<int,int,int>`

我有一个这样的实现:

class Resolver {
    readonly IDictionary<Type, Func<object, object>> _funcs = new Dictionary<Type, Func<object, object>>();

    public Resolver() {
        _funcs.Add(typeof(int), o => (int)o*(int)o);
        _funcs.Add(typeof(string), o => (string)o + (string)o);
        // and so on; 
    }

    public T Invoke<T>(T t1) {
        return (T) _funcs[typeof (T)](t1);
    }

    public T Invoke<T>(T t1, T t2) {
        return (T)_funcs[typeof(T)](t1);
    }
}

但是值类型的性能非常糟糕,因为 Func<,> 的内部实现将 object 作为泛型类型而导致装箱。

有没有办法实现我想要的 public 接口,避免对值类型进行装箱?我也不介意实现中的静态类型安全,但可以没有。

您可以执行以下简单技巧(与您当前的实现相比类型安全性更高或更低):

class Resolver
{
    readonly IDictionary<Type, object> _unaryFuncs = new Dictionary<Type, object>();
    readonly IDictionary<Type, object> _binaryFuncs = new Dictionary<Type, object>();

    public Resolver()
    {
        _unaryFuncs.Add(typeof(int),  new Func<int, int>(o => o * o));
        _unaryFuncs.Add(typeof(string), new Func<string, string(o => o + o));
        _binaryFuncs.Add(typeof(int), new Func<int, int, int>((x, y) => x + y));
        // and so on; 
    }

    public T Invoke<T>(T t1)
    {
        var f = _unaryFuncs[typeof(T)] as Func<T, T>;
        return f(t1);
    }

    public T Invoke<T>(T t1, T t2)
    {
        var f = _binaryFuncs[typeof(T)] as Func<T, T, T>;
        return f(t1, t2);
    }
}

您可能想要添加一些错误检查,例如检查

  1. 一个 T 函数被注册,然后从字典中获取它。
  2. as 施法后不是 null

并添加类型安全注册函数:

public void Register<T>(Func<T, T> unaryFunc)
{
    _unaryFuncs[typeof(T)] = unaryFunc;
}

public void Register<T>(Func<T, T, T> binaryFunc)
{
    _binaryFuncs[typeof(T)] = binaryFunc;
}

您可以使用静态通用变量来缓存您的解析器。在我的快速测试中,这花费了大约三分之一的时间来执行并且避免了装箱。

由于 ResolverCache<Func<int,int>>.Resolver 是不同于 ResolverCache<Func<string,string>>.Resolver 的变量,您可以以类型安全的方式存储不同的解析器。

class Resolver
{
    static class ResolverCache<T>
    {
        public static T Resolver { get; set; }
    }

    void AddResolver<T>(T resolver)
    {
        ResolverCache<T>.Resolver = resolver;
    }

    public Resolver()
    {
        Func<int, int> intResolver = o => (int)o * (int)o;
        Func<int, int, int> intResolver2 = (o, p) => (int)o * (int)p;
        Func<string, string> stringResolver = o => (string)o + (string)o;

        AddResolver(intResolver);
        AddResolver(intResolver2);
        AddResolver(stringResolver);

        // and so on; 
    }

    public T Invoke<T>(T t1)
    {
        var resolver = ResolverCache<Func<T, T>>.Resolver ?? (v => { throw new Exception("No resolver registered."); });
        return resolver(t1);
    }

    public T Invoke<T>(T t1, T t2)
    {
        var resolver = ResolverCache<Func<T, T, T>>.Resolver ?? ((v, u) => { throw new Exception("No resolver registered."); });
        return resolver(t1, t2);
    }
}