From d49e9642df5b09c2075a7f049bec0b07a7b74c19 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Thu, 15 Aug 2024 14:29:01 -0700 Subject: [PATCH] Eliminate allocations from ReflectionUtility.GetImplementedInterfaces --- YamlDotNet/ReflectionExtensions.cs | 23 ++++++++ .../CollectionNodeDeserializer.cs | 4 +- .../DictionaryNodeDeserializer.cs | 2 +- .../EnumerableNodeDeserializer.cs | 2 +- .../ObjectFactories/ObjectFactoryBase.cs | 4 +- .../Utilities/ReflectionUtility.cs | 54 ------------------- 6 files changed, 29 insertions(+), 60 deletions(-) delete mode 100644 YamlDotNet/Serialization/Utilities/ReflectionUtility.cs diff --git a/YamlDotNet/ReflectionExtensions.cs b/YamlDotNet/ReflectionExtensions.cs index f1e472c95..799e9df26 100644 --- a/YamlDotNet/ReflectionExtensions.cs +++ b/YamlDotNet/ReflectionExtensions.cs @@ -49,6 +49,29 @@ public static bool IsGenericTypeDefinition(this Type type) return type.GetTypeInfo().IsGenericTypeDefinition; } + public static Type? GetImplementationOfOpenGenericInterface(this Type type, Type openGenericType) + { + if (!openGenericType.IsGenericType || !openGenericType.IsInterface) + { + // Note we can likely relax this constraint to also allow for matching other types + throw new ArgumentException("The type must be a generic type definition and an interface", nameof(openGenericType)); + } + + // First check if the type itself is the open generic type + if (IsGenericDefinitionOfType(type, openGenericType)) + { + return type; + } + + // Then check the interfaces + return type.FindInterfaces(static (t, context) => IsGenericDefinitionOfType(t, context), openGenericType).FirstOrDefault(); + + static bool IsGenericDefinitionOfType(Type t, object? context) + { + return t.IsGenericType && t.GetGenericTypeDefinition() == (Type)context; + } + } + public static bool IsInterface(this Type type) { return type.GetTypeInfo().IsInterface; diff --git a/YamlDotNet/Serialization/NodeDeserializers/CollectionNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/CollectionNodeDeserializer.cs index 8c9db18ac..289626d67 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/CollectionNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/CollectionNodeDeserializer.cs @@ -47,7 +47,7 @@ public bool Deserialize(IParser parser, Type expectedType, Func)); + var genericCollectionType = expectedType.GetImplementationOfOpenGenericInterface((typeof(ICollection<>))); if (genericCollectionType != null) { var genericArguments = genericCollectionType.GetGenericArguments(); @@ -58,7 +58,7 @@ public bool Deserialize(IParser parser, Type expectedType, Func but not IList - var genericListType = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IList<>)); + var genericListType = expectedType.GetImplementationOfOpenGenericInterface(typeof(IList<>)); canUpdate = genericListType != null; list = (IList?)Activator.CreateInstance(typeof(GenericCollectionToNonGenericAdapter<>).MakeGenericType(itemType), value); } diff --git a/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs index 43d8c9993..f20e19134 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs @@ -43,7 +43,7 @@ public bool Deserialize(IParser parser, Type expectedType, Func)); + var genericDictionaryType = expectedType.GetImplementationOfOpenGenericInterface(typeof(IDictionary<,>)); if (genericDictionaryType != null) { var genericArguments = genericDictionaryType.GetGenericArguments(); diff --git a/YamlDotNet/Serialization/NodeDeserializers/EnumerableNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/EnumerableNodeDeserializer.cs index fc744ed90..d6cd35aaa 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/EnumerableNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/EnumerableNodeDeserializer.cs @@ -38,7 +38,7 @@ public bool Deserialize(IParser parser, Type expectedType, Func)); + var iEnumerable = expectedType.GetImplementationOfOpenGenericInterface(typeof(IEnumerable<>)); if (iEnumerable != expectedType) { value = null; diff --git a/YamlDotNet/Serialization/ObjectFactories/ObjectFactoryBase.cs b/YamlDotNet/Serialization/ObjectFactories/ObjectFactoryBase.cs index 2309dd905..3c363cee3 100644 --- a/YamlDotNet/Serialization/ObjectFactories/ObjectFactoryBase.cs +++ b/YamlDotNet/Serialization/ObjectFactories/ObjectFactoryBase.cs @@ -55,7 +55,7 @@ public virtual void ExecuteOnSerializing(object value) public virtual bool GetDictionary(IObjectDescriptor descriptor, out IDictionary? dictionary, out Type[]? genericArguments) { - var genericDictionaryType = ReflectionUtility.GetImplementedGenericInterface(descriptor.Type, typeof(IDictionary<,>)); + var genericDictionaryType = descriptor.Type.GetImplementationOfOpenGenericInterface(typeof(IDictionary<,>)); if (genericDictionaryType != null) { genericArguments = genericDictionaryType.GetGenericArguments(); @@ -70,7 +70,7 @@ public virtual bool GetDictionary(IObjectDescriptor descriptor, out IDictionary? public virtual Type GetValueType(Type type) { - var enumerableType = ReflectionUtility.GetImplementedGenericInterface(type, typeof(IEnumerable<>)); + var enumerableType = type.GetImplementationOfOpenGenericInterface(typeof(IEnumerable<>)); var itemType = enumerableType != null ? enumerableType.GetGenericArguments()[0] : typeof(object); return itemType; } diff --git a/YamlDotNet/Serialization/Utilities/ReflectionUtility.cs b/YamlDotNet/Serialization/Utilities/ReflectionUtility.cs deleted file mode 100644 index d993e0bba..000000000 --- a/YamlDotNet/Serialization/Utilities/ReflectionUtility.cs +++ /dev/null @@ -1,54 +0,0 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; - -namespace YamlDotNet.Serialization.Utilities -{ - internal static class ReflectionUtility - { - public static Type? GetImplementedGenericInterface(Type type, Type genericInterfaceType) - { - foreach (var interfacetype in GetImplementedInterfaces(type)) - { - if (interfacetype.IsGenericType() && interfacetype.GetGenericTypeDefinition() == genericInterfaceType) - { - return interfacetype; - } - } - return null; - } - - public static IEnumerable GetImplementedInterfaces(Type type) - { - if (type.IsInterface()) - { - yield return type; - } - - foreach (var implementedInterface in type.GetInterfaces()) - { - yield return implementedInterface; - } - } - } -}