ReadOnlyCollection 属性 底层 Collection 在迭代时被修改
ReadOnlyCollection property underlying Collection is modified while iterating
我有以下 属性:
private static Collection<Assembly> _loadedAssemblies = new Collection<Assembly>();
internal static ReadOnlyCollection<Assembly> LoadedAssemblies
{
get { return new ReadOnlyCollection<Assembly>(_loadedAssemblies); }
}
在另一个 class 中循环遍历 LoadedAssemblies
foreach (Assembly assembly in ResourceLoader.LoadedAssemblies)
在遍历程序集时,底层集合 (_loadedAssemblies
) 有时会发生变化,这会导致 System.InvalidOperationException
。使 LoadedAssemblies
安全的首选方法是什么?我无法重现问题,所以我不能尝试。
可以做吗?
internal static ReadOnlyCollection<Assembly> LoadedAssemblies
{
get { return new ReadOnlyCollection<Assembly>(_loadedAssemblies.ToList()); }
}
编辑
public static void Initialize()
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
AddAssembly(assembly);
}
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyLoad += OnAssemblyLoad;
}
private static void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
AddAssembly(args.LoadedAssembly);
}
private static void AddAssembly(Assembly assembly)
{
AssemblyName assemblyName = new AssemblyName(assembly.FullName);
string moduleName = assemblyName.Name;
if (!_doesNotEndWith.Exists(x => moduleName.EndsWith(x, StringComparison.OrdinalIgnoreCase)) &&
_startsWith.Exists(x => moduleName.StartsWith(x, StringComparison.OrdinalIgnoreCase)))
{
if (!_loadedAssemblies.Contains(assembly))
{
_loadedAssemblies.Add(assembly);
}
}
}
ReadOnlyCollection<T>
只是原始集合的包装器,可防止直接修改。但是,如果您在某处公开它,它不会阻止修改基础集合。
由于 ReadOnlyCollection<T>.GetEnumerator
只是为基础集合返回一个枚举器,它具有相同的规则。枚举时不能修改,即不能添加或删除项目。
没有看到您的代码就没有简单的解决方法,防止多个线程同时访问底层集合,f.e。用 lock
。
如果您在 foreach
中修改它,修复起来可能更简单,但我们需要查看该代码。
您在问题中提出的建议会奏效。即:
internal static ReadOnlyCollection<Assembly> LoadedAssemblies
{
get { return new ReadOnlyCollection<Assembly>(_loadedAssemblies.ToList()); }
}
原因是您的问题来自 _loadedAssemblies
在您枚举它时被更改。这当然会发生,因为 ReadOnlyCollection
只是 _loadedAssemblies
的包装器,它使用基础集合的枚举器。
如果您这样做 _loadedAssemblies.ToList()
那么这将创建一个新列表,它是原始 _loadedAssemblies
的副本。它在创建时将具有所有相同的元素,但永远不会再次更新(因为您甚至没有对新集合的引用,您无法修改它)。这意味着当 _loadedAssemblies
更新时,您在 ReadOnlyCollection
中的新列表很幸运地不知道该更改,因此您的枚举将毫无问题地继续到最后。
我有以下 属性:
private static Collection<Assembly> _loadedAssemblies = new Collection<Assembly>();
internal static ReadOnlyCollection<Assembly> LoadedAssemblies
{
get { return new ReadOnlyCollection<Assembly>(_loadedAssemblies); }
}
在另一个 class 中循环遍历 LoadedAssemblies
foreach (Assembly assembly in ResourceLoader.LoadedAssemblies)
在遍历程序集时,底层集合 (_loadedAssemblies
) 有时会发生变化,这会导致 System.InvalidOperationException
。使 LoadedAssemblies
安全的首选方法是什么?我无法重现问题,所以我不能尝试。
可以做吗?
internal static ReadOnlyCollection<Assembly> LoadedAssemblies
{
get { return new ReadOnlyCollection<Assembly>(_loadedAssemblies.ToList()); }
}
编辑
public static void Initialize()
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
AddAssembly(assembly);
}
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyLoad += OnAssemblyLoad;
}
private static void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
AddAssembly(args.LoadedAssembly);
}
private static void AddAssembly(Assembly assembly)
{
AssemblyName assemblyName = new AssemblyName(assembly.FullName);
string moduleName = assemblyName.Name;
if (!_doesNotEndWith.Exists(x => moduleName.EndsWith(x, StringComparison.OrdinalIgnoreCase)) &&
_startsWith.Exists(x => moduleName.StartsWith(x, StringComparison.OrdinalIgnoreCase)))
{
if (!_loadedAssemblies.Contains(assembly))
{
_loadedAssemblies.Add(assembly);
}
}
}
ReadOnlyCollection<T>
只是原始集合的包装器,可防止直接修改。但是,如果您在某处公开它,它不会阻止修改基础集合。
由于 ReadOnlyCollection<T>.GetEnumerator
只是为基础集合返回一个枚举器,它具有相同的规则。枚举时不能修改,即不能添加或删除项目。
没有看到您的代码就没有简单的解决方法,防止多个线程同时访问底层集合,f.e。用 lock
。
如果您在 foreach
中修改它,修复起来可能更简单,但我们需要查看该代码。
您在问题中提出的建议会奏效。即:
internal static ReadOnlyCollection<Assembly> LoadedAssemblies
{
get { return new ReadOnlyCollection<Assembly>(_loadedAssemblies.ToList()); }
}
原因是您的问题来自 _loadedAssemblies
在您枚举它时被更改。这当然会发生,因为 ReadOnlyCollection
只是 _loadedAssemblies
的包装器,它使用基础集合的枚举器。
如果您这样做 _loadedAssemblies.ToList()
那么这将创建一个新列表,它是原始 _loadedAssemblies
的副本。它在创建时将具有所有相同的元素,但永远不会再次更新(因为您甚至没有对新集合的引用,您无法修改它)。这意味着当 _loadedAssemblies
更新时,您在 ReadOnlyCollection
中的新列表很幸运地不知道该更改,因此您的枚举将毫无问题地继续到最后。