| File: System\Resources\Extensions\BinaryFormat\BinaryFormattedObject.TypeResolver.cs | Web Access |
| Project: src\runtime\src\libraries\System.Resources.Extensions\src\System.Resources.Extensions.csproj (System.Resources.Extensions) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Reflection.Metadata; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; #pragma warning disable SYSLIB0050 // Type or member is obsolete namespace System.Resources.Extensions.BinaryFormat; internal sealed partial class BinaryFormattedObject { internal sealed class DefaultTypeResolver : ITypeResolver { private readonly FormatterAssemblyStyle _assemblyMatching; private readonly SerializationBinder? _binder; private readonly Dictionary<string, Assembly> _assemblies = []; private readonly Dictionary<string, Type> _types = []; internal DefaultTypeResolver(Options options) { _assemblyMatching = options.AssemblyMatching; _binder = options.Binder; } /// <summary> /// Resolves the given type name against the specified library. /// </summary> [RequiresUnreferencedCode("Calls System.Reflection.Assembly.GetType(String)")] [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type ITypeResolver.GetType(TypeName typeName) { Debug.Assert(typeName.AssemblyName is not null); if (_types.TryGetValue(typeName.AssemblyQualifiedName, out Type? cachedType)) { return cachedType; } if (_binder?.BindToType(typeName.AssemblyName.FullName, typeName.FullName) is Type binderType) { // BinaryFormatter is inconsistent about what caching behavior you get with binders. // It would always cache the last item from the binder, but wouldn't put the result // in the type cache. This could lead to inconsistent results if the binder didn't // always return the same result for a given set of strings. Choosing to always cache // for performance. _types[typeName.AssemblyQualifiedName] = binderType; return binderType; } if (!_assemblies.TryGetValue(typeName.AssemblyName.FullName, out Assembly? assembly)) { AssemblyName assemblyName = typeName.AssemblyName.ToAssemblyName(); try { assembly = Assembly.Load(assemblyName); } catch { if (_assemblyMatching != FormatterAssemblyStyle.Simple) { throw; } assembly = Assembly.Load(assemblyName.Name!); } _assemblies.Add(typeName.AssemblyName.FullName, assembly); } Type? type = _assemblyMatching != FormatterAssemblyStyle.Simple ? assembly.GetType(typeName.FullName) : GetSimplyNamedTypeFromAssembly(assembly, typeName); _types[typeName.AssemblyQualifiedName] = type ?? throw new SerializationException(SR.Format(SR.Serialization_MissingType, typeName.AssemblyQualifiedName)); return type; } [RequiresUnreferencedCode("Calls System.Reflection.Assembly.GetType(String, Boolean, Boolean)")] private static Type? GetSimplyNamedTypeFromAssembly(Assembly assembly, TypeName typeName) { // Catching any exceptions that could be thrown from a failure on assembly load // This is necessary, for example, if there are generic parameters that are qualified // with a version of the assembly that predates the one available. try { return assembly.GetType(typeName.FullName, throwOnError: false, ignoreCase: false); } catch (TypeLoadException) { } catch (FileNotFoundException) { } catch (FileLoadException) { } catch (BadImageFormatException) { } return Type.GetType(typeName.FullName, ResolveSimpleAssemblyName, new TopLevelAssemblyTypeResolver(assembly).ResolveType, throwOnError: false); static Assembly? ResolveSimpleAssemblyName(AssemblyName assemblyName) { try { return Assembly.Load(assemblyName); } catch { } try { return Assembly.Load(assemblyName.Name!); } catch { } return null; } } private sealed class TopLevelAssemblyTypeResolver { private readonly Assembly _topLevelAssembly; public TopLevelAssemblyTypeResolver(Assembly topLevelAssembly) => _topLevelAssembly = topLevelAssembly; [RequiresUnreferencedCode("Calls System.Reflection.Assembly.GetType(String, Boolean, Boolean)")] public Type? ResolveType(Assembly? assembly, string simpleTypeName, bool ignoreCase) { assembly ??= _topLevelAssembly; return assembly.GetType(simpleTypeName, throwOnError: false, ignoreCase); } } } } #pragma warning restore SYSLIB0050 // Type or member is obsolete