通过反射访问System.Collections.Immutable及FrozenDictionary时遇类型错误
尝试将System.Collections.Immutable作为项目的可选依赖,通过反射实现逻辑:默认使用普通ReadOnlyCollection,若该程序集可用则改用FrozenDictionary(两者都可转换为IReadOnlyDictionary)。
现有反射代码如下:
Assembly assembly = Dependencies.FirstOrDefault(x => x.GetName().Name == "System.Collections.Immutable"); Type staticFrozen = assembly.GetExportedTypes().FirstOrDefault(x => x.Name == "FrozenDictionary" && x.IsAbstract && x.IsSealed); MethodInfo method = staticFrozen.GetMethods().First(x => x.GetParameters().Length == 2); IReadOnlyDictionary<char, float> dict = (IReadOnlyDictionary<char, float>)method.MakeGenericMethod(typeof(char), typeof(float)).Invoke(null, new[] { baseDictionary, null } );
代码已包裹在try-catch块中,程序集通过Assembly.Load(byte[])加载成功,但调用ToFrozenDictionary方法时抛出错误:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.TypeLoadException: Could not find method '.ctor' due to a type load error: VTable setup of type System.Collections.Frozen.EmptyFrozenDictionary
2[TKey,TValue] failed assembly:data-000002C6933245E0 type:EmptyFrozenDictionary2 member:(null)
不确定问题原因(是否存在嵌入式资源或其他依赖导致故障?),希望获取无需硬编码依赖、未安装该组件时也不会崩溃的访问方案。
1. 问题根源
错误源于FrozenDictionary相关类型的依赖链未完整解析:直接通过字节加载System.Collections.Immutable时,它依赖的底层系统组件(如特定版本的System.Runtime、内部辅助类型)未被正确加载,导致内部类型初始化失败。
2. 改进后的反射实现
安全的反射逻辑
逐层做null校验,任何环节失败都降级到ReadOnlyDictionary:
IReadOnlyDictionary<char, float> CreateOptimizedDictionary(Dictionary<char, float> baseDictionary) { try { // 先查找已加载的程序集 var immutableAssembly = AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.GetName().Name.Equals("System.Collections.Immutable", StringComparison.OrdinalIgnoreCase)); // 尝试从嵌入资源加载(如果项目中嵌入了该dll) if (immutableAssembly == null) { var resourceName = "YourProjectNamespace.System.Collections.Immutable.dll"; using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); if (stream == null) return new ReadOnlyDictionary<char, float>(baseDictionary); immutableAssembly = Assembly.Load(stream.ReadAllBytes()); } // 获取FrozenDictionary静态类型(完整命名空间) var frozenDictType = immutableAssembly.GetType("System.Collections.Frozen.FrozenDictionary"); if (frozenDictType == null) return new ReadOnlyDictionary<char, float>(baseDictionary); // 精准定位ToFrozenDictionary重载(避免参数长度匹配错误) var createMethod = frozenDictType.GetMethod( "ToFrozenDictionary", BindingFlags.Public | BindingFlags.Static, new[] { typeof(IEnumerable<KeyValuePair<char, float>>), typeof(IEqualityComparer<char>) }); if (createMethod == null) return new ReadOnlyDictionary<char, float>(baseDictionary); // 调用方法并返回 var genericMethod = createMethod.MakeGenericMethod(typeof(char), typeof(float)); return (IReadOnlyDictionary<char, float>)genericMethod.Invoke(null, new object[] { baseDictionary, null }); } catch (Exception) { // 任何异常都降级到ReadOnlyDictionary return new ReadOnlyDictionary<char, float>(baseDictionary); } }
依赖链处理
如果通过嵌入资源加载程序集:
- 确保嵌入的
System.Collections.Immutable.dll版本与项目的.NET版本兼容 - 若该程序集依赖
System.Memory、System.Runtime.CompilerServices.Unsafe等组件,需一并嵌入并在加载Immutable程序集前优先加载这些依赖
3. 替代方案
NuGet可选依赖配置
在项目文件中设置System.Collections.Immutable为可选依赖,用户安装时可选择是否包含:
<PackageReference Include="System.Collections.Immutable" Version="7.0.0" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
配合条件编译,在代码中直接调用FrozenDictionary,未安装时自动降级。
条件编译实现
通过预编译指令区分两种逻辑:
#if HAVE_IMMUTABLE return baseDictionary.ToFrozenDictionary(); #else return new ReadOnlyDictionary<char, float>(baseDictionary); #endif
在项目文件中添加条件编译符号:
<PropertyGroup> <DefineConstants Condition="Exists('packages\System.Collections.Immutable.7.0.0')">HAVE_IMMUTABLE;$(DefineConstants)</DefineConstants> </PropertyGroup>
内容的提问来源于stack exchange,提问作者totheendoftheworld

