Use concurrent dictionaries
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@@ -17,11 +18,11 @@ namespace Umbraco.Core
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
// Cache the various type lookups
|
||||
private static readonly Dictionary<Type, Type> NullableGenericCache = new Dictionary<Type, Type>();
|
||||
private static readonly Dictionary<CompositeTypeTypeKey, TypeConverter> InputTypeConverterCache = new Dictionary<CompositeTypeTypeKey, TypeConverter>();
|
||||
private static readonly Dictionary<CompositeTypeTypeKey, TypeConverter> DestinationTypeConverterCache = new Dictionary<CompositeTypeTypeKey, TypeConverter>();
|
||||
private static readonly Dictionary<CompositeTypeTypeKey, IConvertible> AssignableTypeCache = new Dictionary<CompositeTypeTypeKey, IConvertible>();
|
||||
private static readonly Dictionary<Type, bool> BoolConvertCache = new Dictionary<Type, bool>();
|
||||
private static readonly ConcurrentDictionary<Type, Type> NullableGenericCache = new ConcurrentDictionary<Type, Type>();
|
||||
private static readonly ConcurrentDictionary<CompositeTypeTypeKey, TypeConverter> InputTypeConverterCache = new ConcurrentDictionary<CompositeTypeTypeKey, TypeConverter>();
|
||||
private static readonly ConcurrentDictionary<CompositeTypeTypeKey, TypeConverter> DestinationTypeConverterCache = new ConcurrentDictionary<CompositeTypeTypeKey, TypeConverter>();
|
||||
private static readonly ConcurrentDictionary<CompositeTypeTypeKey, IConvertible> AssignableTypeCache = new ConcurrentDictionary<CompositeTypeTypeKey, IConvertible>();
|
||||
private static readonly ConcurrentDictionary<Type, bool> BoolConvertCache = new ConcurrentDictionary<Type, bool>();
|
||||
|
||||
private static readonly char[] NumberDecimalSeparatorsToNormalize = { '.', ',' };
|
||||
private static readonly CustomBooleanTypeConverter CustomBooleanTypeConverter = new CustomBooleanTypeConverter();
|
||||
@@ -72,7 +73,7 @@ namespace Umbraco.Core
|
||||
/// <returns>The <see cref="Attempt{T}"/></returns>
|
||||
public static Attempt<T> TryConvertTo<T>(this object input)
|
||||
{
|
||||
var result = TryConvertTo(input, typeof(T));
|
||||
var result = TryConvertTo(input, typeof(T));
|
||||
|
||||
if (result.Success)
|
||||
return Attempt<T>.Succeed((T) result.Result);
|
||||
@@ -101,7 +102,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
return Attempt<object>.Fail();
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (input == null)
|
||||
@@ -162,8 +163,8 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// target is not a generic type
|
||||
{
|
||||
// target is not a generic type
|
||||
|
||||
if (input is string inputString)
|
||||
{
|
||||
@@ -288,7 +289,7 @@ namespace Umbraco.Core
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt64(value2));
|
||||
}
|
||||
|
||||
// TODO: Should we do the decimal trick for short, byte, unsigned?
|
||||
// TODO: Should we do the decimal trick for short, byte, unsigned?
|
||||
|
||||
if (target == typeof(bool))
|
||||
{
|
||||
@@ -651,71 +652,62 @@ namespace Umbraco.Core
|
||||
var normalized = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
|
||||
return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized);
|
||||
}
|
||||
|
||||
// gets a converter for source, that can convert to target, or null if none exists
|
||||
|
||||
// gets a converter for source, that can convert to target, or null if none exists
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static TypeConverter GetCachedSourceTypeConverter(Type source, Type target)
|
||||
{
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
if (InputTypeConverterCache.TryGetValue(key, out var cached))
|
||||
return cached;
|
||||
|
||||
var converter = TypeDescriptor.GetConverter(source);
|
||||
if (converter.CanConvertTo(target))
|
||||
return InputTypeConverterCache.GetOrAdd(key, k =>
|
||||
{
|
||||
InputTypeConverterCache[key] = converter;
|
||||
return converter;
|
||||
}
|
||||
var ksource = k.Type1;
|
||||
var ktarget = k.Type2;
|
||||
|
||||
InputTypeConverterCache[key] = null;
|
||||
return null;
|
||||
var converter = TypeDescriptor.GetConverter(ksource);
|
||||
return converter.CanConvertTo(ktarget) ? converter : null;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// gets a converter for target, that can convert from source, or null if none exists
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static TypeConverter GetCachedTargetTypeConverter(Type source, Type target)
|
||||
{
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
if (DestinationTypeConverterCache.TryGetValue(key, out var cached))
|
||||
return cached;
|
||||
|
||||
var converter = TypeDescriptor.GetConverter(target);
|
||||
if (converter.CanConvertFrom(source))
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
return DestinationTypeConverterCache.GetOrAdd(key, k =>
|
||||
{
|
||||
DestinationTypeConverterCache[key] = converter;
|
||||
return converter;
|
||||
}
|
||||
var ksource = k.Type1;
|
||||
var ktarget = k.Type2;
|
||||
|
||||
DestinationTypeConverterCache[key] = null;
|
||||
return null;
|
||||
var converter = TypeDescriptor.GetConverter(ktarget);
|
||||
return converter.CanConvertFrom(ksource) ? converter : null;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// gets the underlying type of a nullable type, or null if the type is not nullable
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Type GetCachedGenericNullableType(Type type)
|
||||
{
|
||||
if (NullableGenericCache.TryGetValue(type, out var cached))
|
||||
return cached;
|
||||
|
||||
if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
var underlying = Nullable.GetUnderlyingType(type);
|
||||
NullableGenericCache[type] = underlying;
|
||||
return underlying;
|
||||
}
|
||||
|
||||
NullableGenericCache[type] = null;
|
||||
return null;
|
||||
|
||||
return NullableGenericCache.GetOrAdd(type, t
|
||||
=> t.GetGenericTypeDefinition() == typeof(Nullable<>) ? Nullable.GetUnderlyingType(type) : null;
|
||||
}
|
||||
|
||||
|
||||
// gets an IConvertible from source to target type, or null if none exists
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static IConvertible GetCachedAssignableConvertibleResult(object input, Type source, Type target)
|
||||
{
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
return AssignableTypeCache.GetOrAdd(key, k =>
|
||||
{
|
||||
var ksource = k.Type1;
|
||||
var ktarget = k.Type2;
|
||||
|
||||
// FIXME the value we are caching does not depend on the key - WHAT IS THIS?
|
||||
return ktarget.IsAssignableFrom(ksource) && input is IConvertible convertible ? convertible : null;
|
||||
});
|
||||
|
||||
/*
|
||||
if (AssignableTypeCache.TryGetValue(key, out var cached))
|
||||
return cached;
|
||||
return cached;
|
||||
|
||||
if (target.IsAssignableFrom(source) && input is IConvertible convertible)
|
||||
{
|
||||
@@ -724,19 +716,16 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
AssignableTypeCache[key] = null;
|
||||
return null;
|
||||
return null;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// determines whether a type can be converted to boolean
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool GetCanConvertToBooleanResult(Type type)
|
||||
{
|
||||
if (BoolConvertCache.TryGetValue(type, out var cached))
|
||||
return cached;
|
||||
|
||||
var canConvert = CustomBooleanTypeConverter.CanConvertFrom(type);
|
||||
BoolConvertCache[type] = canConvert;
|
||||
return canConvert;
|
||||
return BoolConvertCache.GetOrAdd(type, t
|
||||
=> CustomBooleanTypeConverter.CanConvertFrom(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user