fix: Improved threadsafety for TypeExtensions

This commit is contained in:
Drew Tingen
2020-12-14 15:16:17 -05:00
committed by Chris Cameron
parent 9819c44dcc
commit e61dcd8596

View File

@@ -60,11 +60,17 @@ namespace ICD.Common.Utils.Extensions
}; };
private static readonly Dictionary<Type, Type[]> s_TypeAllTypes; private static readonly Dictionary<Type, Type[]> s_TypeAllTypes;
private static readonly SafeCriticalSection s_TypeAllTypesSection;
private static readonly Dictionary<Type, Type[]> s_TypeBaseTypes; private static readonly Dictionary<Type, Type[]> s_TypeBaseTypes;
private static readonly SafeCriticalSection s_TypeBaseTypesSection;
private static readonly Dictionary<Type, Type[]> s_TypeImmediateInterfaces; private static readonly Dictionary<Type, Type[]> s_TypeImmediateInterfaces;
private static readonly SafeCriticalSection s_TypeImmediateInterfacesSection;
private static readonly Dictionary<Type, Type[]> s_TypeMinimalInterfaces; private static readonly Dictionary<Type, Type[]> s_TypeMinimalInterfaces;
private static readonly SafeCriticalSection s_TypeMinimalInterfacesSection;
private static readonly Dictionary<Type, string> s_TypeToMinimalName; private static readonly Dictionary<Type, string> s_TypeToMinimalName;
private static readonly SafeCriticalSection s_TypeToMinimalNameSection;
private static readonly Dictionary<Type, string> s_TypeToNameWithoutAssemblyDetails; private static readonly Dictionary<Type, string> s_TypeToNameWithoutAssemblyDetails;
private static readonly SafeCriticalSection s_TypeToNameWithoutAssemblyDetailsSection;
/// <summary> /// <summary>
/// Static constructor. /// Static constructor.
@@ -72,11 +78,17 @@ namespace ICD.Common.Utils.Extensions
static TypeExtensions() static TypeExtensions()
{ {
s_TypeAllTypes = new Dictionary<Type, Type[]>(); s_TypeAllTypes = new Dictionary<Type, Type[]>();
s_TypeAllTypesSection = new SafeCriticalSection();
s_TypeBaseTypes = new Dictionary<Type, Type[]>(); s_TypeBaseTypes = new Dictionary<Type, Type[]>();
s_TypeBaseTypesSection = new SafeCriticalSection();
s_TypeImmediateInterfaces = new Dictionary<Type, Type[]>(); s_TypeImmediateInterfaces = new Dictionary<Type, Type[]>();
s_TypeImmediateInterfacesSection = new SafeCriticalSection();
s_TypeMinimalInterfaces = new Dictionary<Type, Type[]>(); s_TypeMinimalInterfaces = new Dictionary<Type, Type[]>();
s_TypeMinimalInterfacesSection = new SafeCriticalSection();
s_TypeToMinimalName = new Dictionary<Type, string>(); s_TypeToMinimalName = new Dictionary<Type, string>();
s_TypeToMinimalNameSection = new SafeCriticalSection();
s_TypeToNameWithoutAssemblyDetails = new Dictionary<Type, string>(); s_TypeToNameWithoutAssemblyDetails = new Dictionary<Type, string>();
s_TypeToNameWithoutAssemblyDetailsSection = new SafeCriticalSection();
} }
/// <summary> /// <summary>
@@ -246,16 +258,24 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
Type[] types; Type[] types;
if (!s_TypeAllTypes.TryGetValue(extends, out types)) s_TypeAllTypesSection.Enter();
try
{ {
types = extends.GetBaseTypes() if (s_TypeAllTypes.TryGetValue(extends, out types))
.Concat(extends.GetInterfaces()) return types;
.Prepend(extends) }
.ToArray(); finally
{
s_TypeAllTypes[extends] = types; s_TypeAllTypesSection.Leave();
} }
types = extends.GetBaseTypes()
.Concat(extends.GetInterfaces())
.Prepend(extends)
.ToArray();
s_TypeAllTypesSection.Execute(() => s_TypeAllTypes[extends] = types);
return types; return types;
} }
@@ -271,11 +291,20 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
Type[] types; Type[] types;
if (!s_TypeBaseTypes.TryGetValue(extends, out types))
s_TypeBaseTypesSection.Enter();
try
{ {
types = GetBaseTypesIterator(extends).ToArray(); if (s_TypeBaseTypes.TryGetValue(extends, out types))
s_TypeBaseTypes[extends] = types; return types;
} }
finally
{
s_TypeBaseTypesSection.Leave();
}
types = GetBaseTypesIterator(extends).ToArray();
s_TypeBaseTypesSection.Execute(() => s_TypeBaseTypes[extends] = types);
return types; return types;
} }
@@ -309,20 +338,30 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
Type[] immediateInterfaces; Type[] immediateInterfaces;
if (!s_TypeImmediateInterfaces.TryGetValue(extends, out immediateInterfaces))
s_TypeImmediateInterfacesSection.Enter();
try
{ {
IEnumerable<Type> allInterfaces = extends.GetInterfaces(); if (s_TypeImmediateInterfaces.TryGetValue(extends, out immediateInterfaces))
return immediateInterfaces;
IEnumerable<Type> childInterfaces =
extends.GetAllTypes()
.Except(extends)
.SelectMany(t => t.GetImmediateInterfaces())
.Distinct();
immediateInterfaces = allInterfaces.Except(childInterfaces).ToArray();
s_TypeImmediateInterfaces[extends] = immediateInterfaces;
} }
finally
{
s_TypeImmediateInterfacesSection.Leave();
}
IEnumerable<Type> allInterfaces = extends.GetInterfaces();
IEnumerable<Type> childInterfaces =
extends.GetAllTypes()
.Except(extends)
.SelectMany(t => t.GetImmediateInterfaces())
.Distinct();
immediateInterfaces = allInterfaces.Except(childInterfaces).ToArray();
s_TypeImmediateInterfacesSection.Execute(() => s_TypeImmediateInterfaces[extends] = immediateInterfaces);
return immediateInterfaces; return immediateInterfaces;
} }
@@ -339,14 +378,24 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
Type[] minimalInterfaces; Type[] minimalInterfaces;
if (!s_TypeMinimalInterfaces.TryGetValue(extends, out minimalInterfaces))
{
Type[] allInterfaces = extends.GetInterfaces();
minimalInterfaces = allInterfaces.Except(allInterfaces.SelectMany(t => t.GetInterfaces()))
.ToArray();
s_TypeMinimalInterfaces[extends] = minimalInterfaces;
s_TypeMinimalInterfacesSection.Enter();
try
{
if (s_TypeMinimalInterfaces.TryGetValue(extends, out minimalInterfaces))
return minimalInterfaces;
} }
finally
{
s_TypeMinimalInterfacesSection.Leave();
}
Type[] allInterfaces = extends.GetInterfaces();
minimalInterfaces = allInterfaces.Except(allInterfaces.SelectMany(t => t.GetInterfaces()))
.ToArray();
s_TypeMinimalInterfacesSection.Execute(() => s_TypeMinimalInterfaces[extends] = minimalInterfaces);
return minimalInterfaces; return minimalInterfaces;
} }
@@ -379,47 +428,44 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param> /// <param name="extends"></param>
/// <returns></returns> /// <returns></returns>
[NotNull] [NotNull]
public static string GetMinimalName([NotNull]this Type extends) public static string GetMinimalName([NotNull] this Type extends)
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
string name; string name;
if (!s_TypeToMinimalName.TryGetValue(extends, out name))
s_TypeToMinimalNameSection.Enter();
try
{ {
// Generics are a pain if (s_TypeToMinimalName.TryGetValue(extends, out name))
if (extends.IsGenericType) return name;
{
string nameWithoutAssemblyDetails = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
int genericStart = nameWithoutAssemblyDetails.IndexOf('[');
if (genericStart < 0)
{
name = nameWithoutAssemblyDetails;
}
else
{
string genericParameterNames =
string.Join("],[", extends.GetGenericArguments().Select(t => t.GetMinimalName()).ToArray());
int genericEnd = nameWithoutAssemblyDetails.LastIndexOf(']');
name = new StringBuilder().Append(nameWithoutAssemblyDetails, 0, genericStart + 2)
.Append(genericParameterNames)
.Append(nameWithoutAssemblyDetails, genericEnd - 1,
nameWithoutAssemblyDetails.Length - genericEnd + 1)
.ToString();
}
}
else
{
name = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
}
s_TypeToMinimalName[extends] = name;
} }
finally
{
s_TypeToMinimalNameSection.Leave();
}
name = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
// Generics are a pain
if (extends.IsGenericType)
{
int genericStart = name.IndexOf('[');
if (genericStart >= 0)
{
string genericParameterNames =
string.Join("],[", extends.GetGenericArguments().Select(t => t.GetMinimalName()).ToArray());
int genericEnd = name.LastIndexOf(']');
name = new StringBuilder().Append(name, 0, genericStart + 2)
.Append(genericParameterNames)
.Append(name, genericEnd - 1,
name.Length - genericEnd + 1)
.ToString();
}
}
s_TypeToMinimalNameSection.Execute(() => s_TypeToMinimalName[extends] = name);
return name; return name;
} }
@@ -436,11 +482,22 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
string name; string name;
if (!s_TypeToNameWithoutAssemblyDetails.TryGetValue(extends, out name))
s_TypeToNameWithoutAssemblyDetailsSection.Enter();
try
{ {
name = RemoveAssemblyDetails(extends.AssemblyQualifiedName); if (s_TypeToNameWithoutAssemblyDetails.TryGetValue(extends, out name))
s_TypeToNameWithoutAssemblyDetails[extends] = name; return name;
} }
finally
{
s_TypeToNameWithoutAssemblyDetailsSection.Leave();
}
name = RemoveAssemblyDetails(extends.AssemblyQualifiedName);
s_TypeToNameWithoutAssemblyDetailsSection.Execute(() => s_TypeToNameWithoutAssemblyDetails[extends] = name);
return name; return name;
} }