In a web application I'm working on I have a class that Map objects to other (different) objects. It is implemented with something like:
public interface IMapper
{
object Map<T>(T item);
}
public class Mapper
{
private static ConcurrentDictionary<Type, Delegate> cache = new ConcurrentDictionary<Type, Delegate>();
public Mapper()
{
// dependencies are injected in the constructor
// not shown here for simplicity
}
public object Map<T>(T item)
{
if (cache.ContainsKey(typeof(T)))
return (cache[typeof(T)] as Func<T, object>)(item);
var method = typeof(Mapper).GetMethod("Map", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(T) }, null);
var @delegate = (Func<T, object>)Delegate.CreateDelegate(typeof(Func<T, object>), this, method);
cache.TryAdd(typeof(T), @delegate);
return @delegate(item);
// I'm trying not to use this
//return method.Invoke(this, new object[] { item });
}
// example methods
public object Map(SomeObject item) { return new object(); }
public object Map(SomeTotallyDifferentObject item) { return new object(); }
}
In order to assess what I believe should be a performance gain (compared to the .Invoke() solution) I tried to generate ten million objects (tested with strings) and then I ran the mapper for all of them and printed out the time elapsed. The results are.
- Direct call: 1.67 s
- Reflection: 14.09 s
- Optimized Reflection: 2.62 s
which is not bad actually but I wonder if I'm testing this correctly.
Test code
var guidList = new List<Guid>();
for(int i = 0; i < 10000000; i++)
{
guidList.Add(Guid.NewGuid());
}
var mapper = new Mapper();
var sw = new Stopwatch();
sw.Start();
var result = stringList.Select(_ => mapper.Map<Guid>(_)).ToList();
sw.Stop();
(Obviously, when I tested the direct method call I changed the select lambda expression to _ => mapper.Map(_)
)
Am I doing it right?
On a side note: I declared the ConcurrentDictionary as ConcurrentDictionary<Type, Delegate>
and so I have to "cast" the delegate part like this (cache[typeof(T)] as Func<T, object>)(item)
when I'm using it. Is there a better way to do this?
Aucun commentaire:
Enregistrer un commentaire