自动获取所有引用的有效程序集(程序集反射辅助类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
namespace YU.Commons
{
/// <summary>
/// 提供反射相关的辅助方法,如程序集查找、加载、判断等。
/// </summary>
public static class ReflectionHelper
{
/// <summary>
/// 根据产品名称获取当前应用域中所有匹配的程序集。
/// </summary>
/// <param name="productName">产品名称(公司名)。</param>
/// <returns>所有匹配的程序集集合。</returns>
public static IEnumerable<Assembly> GetAssembliesByProductName(string productName)
{
var asms = AppDomain.CurrentDomain.GetAssemblies(); // 获取当前应用域中的所有已加载程序集
foreach (var asm in asms) //遍历每个程序集
{
// 获取程序集的公司属性
var asmCompanyAttr = asm.GetCustomAttribute<AssemblyCompanyAttribute>();
if (asmCompanyAttr != null && asmCompanyAttr.Company == productName)
{
yield return asm; // 如果公司名匹配,则返回该程序集
}
}
}

/// <summary>
/// 判断程序集是否为系统程序集(通过公司名是否包含"Microsoft")。
/// </summary>
/// <param name="asm">程序集对象。</param>
/// <returns>是系统程序集返回true,否则false。</returns>
private static bool IsSystemAssembly(Assembly asm) // 判断程序集是否为系统程序集
{
var asmCompanyAttr = asm.GetCustomAttribute<AssemblyCompanyAttribute>(); // 获取程序集的公司属性
if (asmCompanyAttr == null)
{
return false; // 如果没有公司属性,则认为不是系统程序集
}
else
{
string companyName = asmCompanyAttr.Company; // 获取公司名
return companyName.Contains("Microsoft"); // 如果公司名包含"Microsoft",则认为是系统程序集
}
}

/// <summary>
/// 判断指定路径的程序集文件是否为系统程序集。
/// </summary>
/// <param name="asmPath">程序集文件路径。</param>
/// <returns>是系统程序集返回true,否则false。</returns>
private static bool IsSystemAssembly(string asmPath) // 判断指定路径的程序集文件是否为系统程序集
{
var moduleDef = AsmResolver.DotNet.ModuleDefinition.FromFile(asmPath); // 加载程序集文件
var assembly = moduleDef.Assembly; // 获取程序集定义
if (assembly == null)
{
return false; // 如果没有程序集定义,则认为不是系统程序集
}
var asmCompanyAttr = assembly.CustomAttributes.FirstOrDefault(c => c.Constructor?.DeclaringType?.FullName == typeof(AssemblyCompanyAttribute).FullName); // 获取程序集的公司属性
if (asmCompanyAttr == null)
{
return false; // 如果没有公司属性,则认为不是系统程序集
}
var companyName = ((AsmResolver.Utf8String)asmCompanyAttr.Signature?.FixedArguments[0]?.Element)?.Value;// 获取公司名
if (companyName == null)
{
return false;// 如果公司名为空,则认为不是系统程序集
}
return companyName.Contains("Microsoft"); // 如果公司名包含"Microsoft",则认为是系统程序集
}

/// <summary>
/// 判断指定文件是否为托管程序集(.NET程序集)。
/// </summary>
/// <param name="file">文件路径。</param>
/// <returns>是托管程序集返回true,否则false。</returns>
private static bool IsManagedAssembly(string file) // 判断指定文件是否为托管程序集
{
using var fs = File.OpenRead(file);// 打开文件流
using PEReader peReader = new PEReader(fs);// 创建PEReader对象读取PE文件
return peReader.HasMetadata && peReader.GetMetadataReader().IsAssembly; // 如果PE文件有元数据且是程序集,则认为是托管程序集
}

/// <summary>
/// 尝试加载指定路径的程序集。
/// </summary>
/// <param name="asmPath">程序集文件路径。</param>
/// <returns>加载成功返回程序集对象,否则返回null。</returns>
private static Assembly? TryLoadAssembly(string asmPath)
{
AssemblyName asmName = AssemblyName.GetAssemblyName(asmPath);
Assembly? asm = null;
try
{
asm = Assembly.Load(asmName);
}
catch (BadImageFormatException ex)
{
Debug.WriteLine(ex);
}
catch (FileLoadException ex)
{
Debug.WriteLine(ex);
}
if (asm == null)
{
try
{
asm = Assembly.LoadFile(asmPath);
}
catch (BadImageFormatException ex)
{
Debug.WriteLine(ex);
}
catch (FileLoadException ex)
{
Debug.WriteLine(ex);
}
}
return asm;
}

/// <summary>
/// 获取所有引用的程序集(可选跳过系统程序集),包括当前目录下的所有托管程序集。
/// </summary>
/// <param name="skipSystemAssemblies">是否跳过系统程序集。</param>
/// <returns>所有引用的程序集集合。</returns>
public static IEnumerable<Assembly> GetAllReferencedAssemblies(bool skipSystemAssemblies = true)
{
Assembly? rootAssembly = Assembly.GetEntryAssembly();
if (rootAssembly == null)
{
rootAssembly = Assembly.GetExecutingAssembly();
}
var returnAssemblies = new HashSet<Assembly>(new AssemblyEquality());
var loadedAssemblies = new HashSet<string>();
var assembliesToCheck = new Queue<Assembly>();
assembliesToCheck.Enqueue(rootAssembly);
// 检查根程序集是否需要加入
if (skipSystemAssemblies && IsSystemAssembly(rootAssembly) != false)
{
if (!IsValid(rootAssembly))
{
returnAssemblies.Add(rootAssembly);
}
}
// 广度优先遍历所有引用的程序集
while (assembliesToCheck.Any())
{
var assemblyToCheck = assembliesToCheck.Dequeue();
foreach (var reference in assemblyToCheck.GetReferencedAssemblies())
{
if (!loadedAssemblies.Contains(reference.FullName))
{
var assembly = Assembly.Load(reference);
if (skipSystemAssemblies && IsSystemAssembly(assembly))
{
continue;
}
assembliesToCheck.Enqueue(assembly);
loadedAssemblies.Add(reference.FullName);
if (IsValid(assembly))
{
returnAssemblies.Add(assembly);
}
}
}
}
// 扫描当前目录下所有托管程序集
var asmsInBaseDir = Directory.EnumerateFiles(AppContext.BaseDirectory,
"*.dll", new EnumerationOptions { RecurseSubdirectories = true });
foreach (var asmPath in asmsInBaseDir)
{
if (!IsManagedAssembly(asmPath))
{
continue;
}
AssemblyName asmName = AssemblyName.GetAssemblyName(asmPath);
if (returnAssemblies.Any(x => AssemblyName.ReferenceMatchesDefinition(x.GetName(), asmName)))
{
continue;
}
if (skipSystemAssemblies && IsSystemAssembly(asmPath))
{
continue;
}
Assembly? asm = TryLoadAssembly(asmPath);
if (asm == null)
{
continue;
}
if (!IsValid(asm))
{
continue;
}
if (skipSystemAssemblies && IsSystemAssembly(asm))
{
continue;
}
returnAssemblies.Add(asm);
}
return returnAssemblies.ToArray();
}

/// <summary>
/// 判断程序集是否有效(能否正常获取类型信息)。
/// </summary>
/// <param name="asm">程序集对象。</param>
/// <returns>有效返回true,否则false。</returns>
private static bool IsValid(Assembly asm)
{
try
{
asm.GetTypes();
asm.DefinedTypes.ToList();
return true;
}
catch (ReflectionTypeLoadException)
{
return false;
}
}
}

/// <summary>
/// 用于比较程序集是否相等的辅助类(通过程序集名称)。
/// </summary>
class AssemblyEquality : EqualityComparer<Assembly>
{
public override bool Equals(Assembly? x, Assembly? y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return AssemblyName.ReferenceMatchesDefinition(x.GetName(), y.GetName());
}
public override int GetHashCode([DisallowNull] Assembly obj)
{
return obj.GetName().FullName.GetHashCode();
}
}
}

总体结构说明

这个类提供了:

GetAssembliesByProductName:获取当前已经加载的程序集中,公司名等于指定的程序集。
IsSystemAssembly(Assembly)/IsSystemAssembly(string):判断是否为系统程序集(通过公司名判断)
IsManagedAssembly:判断某.dll文件是否为托管程序集。
TryLoadAssembly:安全加载一个程序集。
GetAllReferencedAssemblies:主方法:递归获取所有引用程序集,并加载当前目录中有效.dll文件。
IsValid:判断程序集是否有效(能否获取类型)。
AssemblyEquality:对程序集进行比较,避免重复添加。

分析每个部分功能与逻辑

GetAssembliesByProductName

1
2
3
4
5
6
7
8
9
10
11
12
13
public static IEnumerable<Assembly> GetAssembliesByProductName(string productName) 
{
var asms = AppDomain.CurrentDomain.GetAssemblies(); // 获取当前应用域中的所有已加载程序集
foreach (var asm in asms) //遍历每个程序集
{
// 获取程序集的公司属性
var asmCompanyAttr = asm.GetCustomAttribute<AssemblyCompanyAttribute>();
if (asmCompanyAttr != null && asmCompanyAttr.Company == productName)
{
yield return asm; // 如果公司名匹配,则返回该程序集
}
}
}

Enumerable<Assembly> 是一个泛型可枚举类型,表示一个 可以遍历的程序集(System.Reflection.Assembly)集合,通常用于反射操作中,比如加载、筛选、注册程序集等场景。
从当前应用域中获取所有已加载的程序集(AppDomain.CurrentDomain.GetAssembles())
检查每个程序集的[AssemblyCompany]特性(公司名)
返回公司名等于指定值的程序集
用于过滤出“公司内开发”的程序集。

IsSystemAssembly(重载方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
private static bool IsSystemAssembly(Assembly asm) // 判断程序集是否为系统程序集
{
var asmCompanyAttr = asm.GetCustomAttribute<AssemblyCompanyAttribute>(); // 获取程序集的公司属性
if (asmCompanyAttr == null)
{
return false; // 如果没有公司属性,则认为不是系统程序集
}
else
{
string companyName = asmCompanyAttr.Company; // 获取公司名
return companyName.Contains("Microsoft"); // 如果公司名包含"Microsoft",则认为是系统程序集
}
}

IsSystemAssembly(Assembly asm)通过AssemblyCompanyAttribute判断是否包含“Microsoft”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static bool IsSystemAssembly(string asmPath) // 判断指定路径的程序集文件是否为系统程序集
{
var moduleDef = AsmResolver.DotNet.ModuleDefinition.FromFile(asmPath); // 加载程序集文件
var assembly = moduleDef.Assembly; // 获取程序集定义
if (assembly == null)
{
return false; // 如果没有程序集定义,则认为不是系统程序集
}
var asmCompanyAttr = assembly.CustomAttributes.FirstOrDefault(c => c.Constructor?.DeclaringType?.FullName == typeof(AssemblyCompanyAttribute).FullName); // 获取程序集的公司属性
if (asmCompanyAttr == null)
{
return false; // 如果没有公司属性,则认为不是系统程序集
}
var companyName = ((AsmResolver.Utf8String)asmCompanyAttr.Signature?.FixedArguments[0]?.Element)?.Value;// 获取公司名
if (companyName == null)
{
return false;// 如果公司名为空,则认为不是系统程序集
}
return companyName.Contains("Microsoft"); // 如果公司名包含"Microsoft",则认为是系统程序集
}

IsSystemAssembly(string asmPath)使用AsmResolver读取.dll元数据,再检查是否是系统程序集。
assembly.CustomAttributes:获取程序集定义上的所有自定义特性(如 [AssemblyCompany], [AssemblyTitle] 等)
.FirstOrDefault(…):从中取出第一个符合条件的特性,找不到就返回null
c.Constructor?.DeclaringType?.FullName:获取特性的构造函数所属类型的完整名称(及特性类型本身的FullName)
typeof(AssemblyCompanyAattribute).FullName:获取AssemblyCompanyAttribute的完整名称,即 “System.Reflection.AssemblyCompanyAttribute”

asmCompanyAttr.Signature:这是一个 CustomAttributeSignature 对象,表示这个特性的构造函数签名,包括参数类型、值等。
.FixedArguments[0]:这是获取第一个固定参数。对于 [AssemblyCompany(“OpenAI”)],这个第一个参数就是 “OpenAI”。
.Element:FixedArguments[0].Element 是这个参数的值,类型是 ICustomAttributeType,但在字符串特性的场景下,它其实就是 Utf8String 类型的对象。
(AsmResolver.Utf8String)…:将 Element 显式转换为 Utf8String,这是 AsmResolver 用来表示字符串的一种类型(和 .NET 的 string 类似,但内部是 UTF-8 编码结构)。
?.Value:取出字符串的值。

这两个方法允许你在这两种场景下都判断程序集是否为“系统提供”(避免处理不必要的系统程序集)

IsManagedAssembly

1
2
3
4
5
6
private static bool IsManagedAssembly(string file)  // 判断指定文件是否为托管程序集
{
using var fs = File.OpenRead(file);// 打开文件流
using PEReader peReader = new PEReader(fs);// 创建PEReader对象读取PE文件
return peReader.HasMetadata && peReader.GetMetadataReader().IsAssembly; // 如果PE文件有元数据且是程序集,则认为是托管程序集
}

使用 System.Reflection.PortableExecutable.PEReader 判断 .dll 是否是托管程序集
核心判断是:是否有元数据 (peReader.HasMetadata) 且为程序集 (IsAssembly)

peReader.HasMetadata:文件中包含 .NET 元数据(说明是托管代码的一部分)
IsAssembly:文件表示的是一个 程序集(即 .dll 或 .exe 中包含 Assembly 清单)

这样可以避免尝试加载本地DLL、C++DLL等非.NET程序集。

TryLoadAssembly

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private static Assembly? TryLoadAssembly(string asmPath) //尝试从指定路径加载一个 .NET 程序集
{
AssemblyName asmName = AssemblyName.GetAssemblyName(asmPath);//读取程序集的元信息
Assembly? asm = null;
try
{
asm = Assembly.Load(asmName);
}
catch (BadImageFormatException ex)
{
Debug.WriteLine(ex);
}
catch (FileLoadException ex)
{
Debug.WriteLine(ex);
}
if (asm == null)
{
try
{
asm = Assembly.LoadFile(asmPath);
}
catch (BadImageFormatException ex)
{
Debug.WriteLine(ex);
}
catch (FileLoadException ex)
{
Debug.WriteLine(ex);
}
}
return asm;
}

使用 Assembly.Load(AssemblyName) 加载
若失败,则尝试 Assembly.LoadFile 加载
捕获并记录常见错误,如 BadImageFormatException 和 FileLoadException

AssemblyName.GetAssemblyName(asmPath):这一步尝试从文件中读取程序集的元信息,比如它的名称、版本、文化、公钥等。不会真正加载程序集,只是获取它的标识。
Assembly.Load(asmName):这是根据程序集名称(而不是文件路径)尝试从 当前 AppDomain 的探测路径中加载程序集。如果已经加载过,它会直接返回。
BadImageFormatException: 文件不是有效的 .NET 程序集(可能是原生 DLL)
FileLoadException: 可能是文件锁定、权限问题、版本冲突等
Assembly.LoadFile(asmPath):如果第一次加载失败(返回 null),就使用 Assembly.LoadFile 直接从绝对路径加载程序集。

避免程序因为一个非法的 .dll 崩溃,同时仍尽可能加载更多程序集。

IsValid

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static bool IsValid(Assembly asm)
{
try
{
asm.GetTypes(); // 尝试获取程序集中的所有类型
asm.DefinedTypes.ToList(); // 尝试枚举所有类型定义
return true; // 没异常,就说明有效
}
catch (ReflectionTypeLoadException ex)
{
Debug.WriteLine($"程序集加载失败: {asm.FullName} - {ex.Message}");
return false; // 如果无法加载某些类型,说明程序集可能不完整或有依赖问题
}
}

通过调用 GetTypes 和 DefinedTypes 来确保程序集中的类型是可访问的
捕获 ReflectionTypeLoadException 以处理不完整的程序集

AssemblyEquality

1
2
3
4
5
6
7
8
9
10
11
12
13
class AssemblyEquality : EqualityComparer<Assembly>
{
public override bool Equals(Assembly? x, Assembly? y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return AssemblyName.ReferenceMatchesDefinition(x.GetName(), y.GetName());
}
public override int GetHashCode([DisallowNull] Assembly obj)
{
return obj.GetName().FullName.GetHashCode();
}
}

比较两个程序集是否相等,使用 AssemblyName.ReferenceMatchesDefinition 来确保版本等信息一致
用 AssemblyName.ReferenceMatchesDefinition 判断两个程序集是否“等价”,并且基于 AssemblyName.FullName 提供稳定的 HashCode。
用于 HashSet<Assembly> 去重时,自定义判断逻辑。

GetAllReferencedAssemblies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public static IEnumerable<Assembly> GetAllReferencedAssemblies(bool skipSystemAssemblies = true)
{
Assembly? rootAssembly = Assembly.GetEntryAssembly();
if (rootAssembly == null)
{
rootAssembly = Assembly.GetExecutingAssembly();
}
var returnAssemblies = new HashSet<Assembly>(new AssemblyEquality());
var loadedAssemblies = new HashSet<string>();
var assembliesToCheck = new Queue<Assembly>();
assembliesToCheck.Enqueue(rootAssembly);
// 检查根程序集是否需要加入
if (skipSystemAssemblies && IsSystemAssembly(rootAssembly) != false)
{
if (!IsValid(rootAssembly))
{
returnAssemblies.Add(rootAssembly);
}
}
// 广度优先遍历所有引用的程序集
while (assembliesToCheck.Any())
{
var assemblyToCheck = assembliesToCheck.Dequeue();
foreach (var reference in assemblyToCheck.GetReferencedAssemblies())
{
if (!loadedAssemblies.Contains(reference.FullName))
{
var assembly = Assembly.Load(reference);
if (skipSystemAssemblies && IsSystemAssembly(assembly))
{
continue;
}
assembliesToCheck.Enqueue(assembly);
loadedAssemblies.Add(reference.FullName);
if (IsValid(assembly))
{
returnAssemblies.Add(assembly);
}
}
}
}
// 扫描当前目录下所有托管程序集
var asmsInBaseDir = Directory.EnumerateFiles(AppContext.BaseDirectory,
"*.dll", new EnumerationOptions { RecurseSubdirectories = true });
foreach (var asmPath in asmsInBaseDir)
{
if (!IsManagedAssembly(asmPath))
{
continue;
}
AssemblyName asmName = AssemblyName.GetAssemblyName(asmPath);
if (returnAssemblies.Any(x => AssemblyName.ReferenceMatchesDefinition(x.GetName(), asmName)))
{
continue;
}
if (skipSystemAssemblies && IsSystemAssembly(asmPath))
{
continue;
}
Assembly? asm = TryLoadAssembly(asmPath);
if (asm == null)
{
continue;
}
if (!IsValid(asm))
{
continue;
}
if (skipSystemAssemblies && IsSystemAssembly(asm))
{
continue;
}
returnAssemblies.Add(asm);
}
return returnAssemblies.ToArray();
}
  1. 初始化
    取入口程序集Assembly.GetEntryAssembly(),找不到就用当前正在执行的程序集。
    创建 returnAssemblies 集合用于去重和存放结果
    创建队列 assembliesToCheck 广度优先遍历依赖关系

    1
    2
    3
    4
    var returnAssemblies = new HashSet<Assembly>(new AssemblyEquality()); // 结果集合,去重
    var loadedAssemblies = new HashSet<string>(); // 已加载的程序集名,防重复加载
    var assembliesToCheck = new Queue<Assembly>(); // 广度优先队列
    assembliesToCheck.Enqueue(rootAssembly);

    assembliesToCheck.Enqueue(rootAssembly):把根程序集放入队列 assembliesToCheck 中,作为后续要处理的第一个程序集。

  2. 检查程序集是否加入

1
2
3
4
5
6
7
if (skipSystemAssemblies && IsSystemAssembly(rootAssembly) != false)
{
if (!IsValid(rootAssembly))
{
returnAssemblies.Add(rootAssembly);
}
}

如果启用了跳过系统程序集(skipSystemAssemblies = true),并且 rootAssembly 被识别为系统程序集,但它又是“无效”的,那就强制加入这个程序集。

  1. 递归加载引用程序集

遍历当前队列中的程序集引用,加载未加载的引用程序集。
使用 Assembly.Load(AssemblyName) 加载引用。
加入队列继续递归。
判断是否为系统程序集、是否有效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
while (assembliesToCheck.Any())
{
var assemblyToCheck = assembliesToCheck.Dequeue();
foreach (var reference in assemblyToCheck.GetReferencedAssemblies())
{
if (!loadedAssemblies.Contains(reference.FullName))
{
var assembly = Assembly.Load(reference);
if (skipSystemAssemblies && IsSystemAssembly(assembly))
{
continue;
}
assembliesToCheck.Enqueue(assembly);
loadedAssemblies.Add(reference.FullName);
if (IsValid(assembly))
{
returnAssemblies.Add(assembly);
}
}
}
}

while (assembliesToCheck.Any()):
这是 广度优先遍历 的主循环条件:
assembliesToCheck 是一个 Queue<Assembly>,存储待处理的程序集;只要队列不为空,就继续处理。
var assemblyToCheck = assembliesToCheck.Dequeue();:从队列中取出一个待检查的程序集。
assemblyToCheck.GetReferencedAssemblies():获取当前程序集引用的所有其他程序集.
if (!loadedAssemblies.Contains(reference.FullName)):判断当前引用程序集是否已处理过:
loadedAssemblies 是一个 HashSet<string>;
避免重复加载和无限循环。
var assembly = Assembly.Load(reference):尝试根据名称加载程序集:
使用 Assembly.Load(AssemblyName) 会从当前的应用域中加载已有的,或从 probing path 中查找。
if (skipSystemAssemblies && IsSystemAssembly(assembly)):跳过系统程序集的条件判断:
如果用户设置了 skipSystemAssemblies = true;
并且当前程序集是系统程序集(比如 Microsoft、System 开头);
那就 continue,直接跳过不处理。
assembliesToCheck.Enqueue(assembly):将新加载的程序集加入待处理队列,递归处理它的引用程序集。

loadedAssemblies.Add(reference.FullName):标记这个程序集已经处理过。
if (IsValid(assembly)) { returnAssemblies.Add(assembly); }:最后判断这个程序集是否是“有效”的(没有类型加载错误等),是的话加入最终结果集中。

  1. 额外加载当前目录下的 DLL 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var asmsInBaseDir = Directory.EnumerateFiles(AppContext.BaseDirectory,
"*.dll", new EnumerationOptions { RecurseSubdirectories = true });
foreach (var asmPath in asmsInBaseDir)
{
if (!IsManagedAssembly(asmPath))
{
continue;
}
AssemblyName asmName = AssemblyName.GetAssemblyName(asmPath);
if (returnAssemblies.Any(x => AssemblyName.ReferenceMatchesDefinition(x.GetName(), asmName)))
{
continue;
}
if (skipSystemAssemblies && IsSystemAssembly(asmPath))
{
continue;
}
Assembly? asm = TryLoadAssembly(asmPath);
if (asm == null)
{
continue;
}
if (!IsValid(asm))
{
continue;
}
if (skipSystemAssemblies && IsSystemAssembly(asm))
{
continue;
}
returnAssemblies.Add(asm);
}
return returnAssemblies.ToArray();

遍历当前应用目录的所有 .dll
判断是否为托管程序集
判断是否为系统程序集
判断是否已经加载
通过 TryLoadAssembly 尝试加载
判断是否有效(能否访问类型定义)

这是为了把那些没有被主动引用,但仍在部署目录中的程序集加载进来。
使用 AppContext.BaseDirectory 获取运行目录,递归查找子目录中所有 .dll 文件
用 PEReader 判断是不是托管程序集
if (returnAssemblies.Any(x =>:防止重复加载同一个程序集(即使路径不同,只要 AssemblyName 一致,就视为相同)

使用场景

场景 示例
模块化注册 自动扫描实现了 IModuleInitializer 的类型并初始化
插件系统 加载插件目录的所有 .NET 程序集
自动依赖注入 根据命名空间、接口特征批量注册服务
动态发现服务 分布式架构中加载远程/本地服务模块