Until I know how you want to handle multi key dictionaries in your
code base I'm not going to submit a patch. Below is how I did it with
a class I usually use for that, more details at:
http://www.codeproject.com/KB/recipes/ClassKey.aspx
If anyone is interested, the LINQ expressions used in the LCG have a
checked/unchecked version which is why it didn't have the overflow
issue. As far as I can tell type converters don't have that feature
and using an unchecked block doesn't stop it from throwing an
exception. When the type converters are cached they are better than
the cached LCG unless I'm doing something wrong here.
Performance from worst to best, with the original test rows plus one
more for overflow condition:
10K iterations * 5 tests per iteration:
LCG compiled each time - 8043ms
TypeConverter with try/catch : 869ms
LCG cached: 143ms
TypeConverter without try/catch (exception on overflow): 82ms
Type converter with try/catch cached: 23ms
class Program
{
static void Main(string[] args)
{
RunComparisons(CreateComparerLCG);
RunComparisons(CreateComparerTypeConverter);
RunComparisons(CreateComparerLCGCachedClass);
RunComparisons(CreateComparerTypeConverterCached);
private class CacheKey : ClassKey<CacheKey>
{
public Type FirstType = null;
public Type SecondType = null;
public override object[] GetKeyValues()
{
return new object[] { FirstType, SecondType };
}
}
private class ComparerCacheClass : Dictionary<CacheKey,
Func<object, object, bool>> { }
private static readonly ComparerCacheClass comparerCacheClass
= new ComparerCacheClass();
private static Func<object, object, bool>
CreateComparerLCGCachedClass(Type firstType, Type secondType)
{
if (firstType == secondType)
return Equals;
var firstParameter = Expression.Parameter(typeof(object),
"first");
var secondParameter = Expression.Parameter(typeof(object),
"second");
var equalExpression = Expression.Equal(Expression.Convert
(firstParameter, firstType),
Expression.Convert(Expression.Convert(secondParameter,
secondType), firstType));
Func<object, object, bool> compareExpression = null;
CacheKey key = new CacheKey { FirstType = firstType,
SecondType = secondType };
if (!comparerCacheClass.TryGetValue(key, out
compareExpression))
{
compareExpression = Expression.Lambda<Func<object,
object, bool>>(equalExpression, firstParameter,
secondParameter).Compile();
comparerCacheClass.Add(key, compareExpression);
}
return compareExpression;
}
private static Func<object, object, bool>
CreateComparerTypeConverterCached(Type firstType, Type secondType)
{
if (firstType == secondType)
return Equals;
Func<object, object, bool> compareExpression = null;
CacheKey key = new CacheKey { FirstType = firstType,
SecondType = secondType };
if (!comparerCacheClass.TryGetValue(key, out
compareExpression))
{
TypeConverter converterFirstType =
TypeDescriptor.GetConverter(firstType);
TypeConverter converterSecondType =
TypeDescriptor.GetConverter(secondType);
compareExpression = delegate(object first, object
second)
{
try
{
return object.Equals(
first,
converterFirstType.ConvertTo(second,
firstType)
);
}
catch (OverflowException)
{
return object.Equals(
second,
converterSecondType.ConvertTo(first,
secondType)
);
}
};
comparerCacheClass.Add(key, compareExpression);
}
return compareExpression;