2015-05-02 4 views
1

Предположим, что у меня есть некоторый класс, который вызывает Func, основанный на родовом типе переданного ему, и у меня есть нужный общедоступный интерфейс для этого класса что-то вроде этого:Как избежать бокс типов значений

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>` 

I есть реализация так:

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, имеющими в качестве родовых типов.

Есть ли способ реализовать мой желаемый публичный интерфейс, избегая бокса для типов значений? Я также не буду возражать против безопасности статического типа внутри реализации, но без него.

ответ

4

Вы могли бы сделать следующий простой трюк (не каким-либо более или менее типобезопасен, чем текущая реализация):

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. не null после as литье.

И добавить типизированные регистрационные функции:

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; 
} 
+0

Это отличный способ избежать моей предполагаемой проблемы. Тем не менее, это ясно, основываясь на моем собственном тестировании, что бокс не является основной причиной проблемы производительности, которую я испытываю. Тем не менее, я ценю ответ. Спасибо! – jdphenix

2

Вы можете использовать статическую родовые переменный кэшировать распознаватель. В моем быстром тестировании это заняло около трети времени, чтобы выполнить и избежать бокса.

С 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); 
    } 
} 
+0

+1 Отлично. Я искал способ пройти через дженерики, не допуская бокса, и это дало мне точно то, что мне нужно для решения проблемы. Спасибо! – Josh

Смежные вопросы