如何避免值类型的装箱
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);
}
}
您可能想要添加一些错误检查,例如检查
- 一个
T
函数被注册,然后从字典中获取它。
- 在
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);
}
}
假设我有一些 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);
}
}
您可能想要添加一些错误检查,例如检查
- 一个
T
函数被注册,然后从字典中获取它。 - 在
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);
}
}