1. 集合过滤的基础方法
过滤就像淘金的筛子,只不过我们不是挑金块,而是从集合里挑出我们想要的元素。比如说,我们有个用户列表——想找出成年的,或者只要学生,或者只要喜欢咖啡的(程序员嘛)。选出合适的元素,这种需求到处都有:从数据库操作到处理用户输入都用得上。
咱们来看看C#里各种过滤的方法。学的过程中,我们会不断完善我们的控制台小程序,给它加各种过滤器。
用循环手动过滤
先来最简单最经典的方式:用foreach循环过滤:
// 例子:有个数字列表,只要偶数
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
List<int> evenNumbers = new List<int>();
foreach (int number in numbers)
{
if (number % 2 == 0) // 如果是偶数
{
evenNumbers.Add(number);
}
}
Console.WriteLine("偶数:");
foreach (int n in evenNumbers)
{
Console.WriteLine(n);
}
这种方式一直都能用,也很好懂,就是不够简洁,也不算“现代”。
为什么手动过滤有时候不方便?
如果你有另一个集合,或者过滤条件变了,或者你要写好几个过滤器——代码就会很重复。我们都想让代码更简洁、更好读、更模块化。
2. 按复杂条件过滤
假设我们有一堆对象。还是拿我们的小程序里的用户举例。
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
声明一个用户列表:
List<User> users = new List<User>
{
new User { Name = "安雅", Age = 17, IsStudent = true },
new User { Name = "鲍里斯", Age = 21, IsStudent = false },
new User { Name = "维卡", Age = 19, IsStudent = true },
new User { Name = "格列布", Age = 25, IsStudent = false }
};
例子1:选出所有学生
List<User> students = new List<User>();
foreach (User user in users)
{
if (user.IsStudent)
students.Add(user);
}
Console.WriteLine("学生名单:");
foreach (User user in students)
{
Console.WriteLine($"{user.Name} ({user.Age})");
}
例子2:选出成年的学生
List<User> adults = new List<User>();
foreach (User user in users)
{
if (user.Age >= 18 && user.IsStudent)
adults.Add(user);
}
Console.WriteLine("成年学生:");
foreach (User user in adults)
{
Console.WriteLine($"{user.Name} ({user.Age})");
}
如果过滤条件是动态的怎么办?
可以把过滤条件单独写成方法,然后在循环里用:
bool IsAdult(User u) { return u.Age >= 18; }
bool IsStudent(User u) { return u.IsStudent; }
List<User> filtered = new List<User>();
foreach (User user in users)
{
if (IsAdult(user) && IsStudent(user))
filtered.Add(user);
}
3. 字典里的按键过滤
列表和数组很方便,但我们经常也会用到字典。
比如说,我们有个字典,key是用户名,value是年龄:
Dictionary<string, int> ageByName = new Dictionary<string, int>
{
["安雅"] = 17,
["鲍里斯"] = 21,
["维卡"] = 19,
["格列布"] = 25
};
任务:只选18+的用户
Dictionary<string, int> adults = new Dictionary<string, int>();
foreach (var pair in ageByName)
{
if (pair.Value >= 18)
adults.Add(pair.Key, pair.Value);
}
Console.WriteLine("成年人:");
foreach (var pair in adults)
{
Console.WriteLine($"{pair.Key}: {pair.Value} 岁");
}
任务:选出名字以“维”开头的用户
Dictionary<string, int> namesWithV = new Dictionary<string, int>();
foreach (var pair in ageByName)
{
if (pair.Key.StartsWith("维"))
namesWithV.Add(pair.Key, pair.Value);
}
Console.WriteLine("以‘维’开头的名字:");
foreach (var pair in namesWithV)
{
Console.WriteLine($"{pair.Key}: {pair.Value} 岁");
}
4. 过滤的原理
过滤流程怎么跑的
┌──────────────────────────────────┐
│ 列表: [1, 2, 3, 4, 5, 6] │
└──────────────────────────────────┘
│
▼
[检查: n % 2 == 0]
│
▼
┌──────────────────────────────────┐
│ 结果: [2, 4, 6] │
└──────────────────────────────────┘
过滤器组合:按不同条件分步过滤
你可以分步过滤,想一步步筛数据也行:
// 先只要学生
List<User> onlyStudents = new List<User>();
foreach (User user in users)
{
if (user.IsStudent)
onlyStudents.Add(user);
}
// 再从里面挑出成年的
List<User> adultStudents = new List<User>();
foreach (User user in onlyStudents)
{
if (user.Age >= 18)
adultStudents.Add(user);
}
或者一步到位:
List<User> adultStudents = new List<User>();
foreach (User user in users)
{
if (user.IsStudent && user.Age >= 18)
adultStudents.Add(user);
}
排序和其他操作
排序可以用Sort方法:
// 按年龄排序的成年学生
adultStudents.Sort((a, b) => a.Age.CompareTo(b.Age));
排序的细节我们下节课再聊!
5. 用户场景下的过滤
1. 过滤用户输入
比如说,我们的程序让用户输入一串分数,要选出所有“5分”。
Console.Write("输入分数,用空格隔开: ");
string input = Console.ReadLine();
List<int> grades = new List<int>();
foreach (string s in input.Split(' '))
{
if (int.TryParse(s, out int grade))
grades.Add(grade);
}
List<int> fives = new List<int>();
foreach (int grade in grades)
{
if (grade == 5)
fives.Add(grade);
}
Console.WriteLine("5分:");
foreach (var grade in fives)
{
Console.WriteLine(grade);
}
2. 多条件过滤
任务:选出18到22岁(含)的学生用户。
List<User> filtered = new List<User>();
foreach (User user in users)
{
if (user.IsStudent && user.Age >= 18 && user.Age <= 22)
filtered.Add(user);
}
3. 按元素是否包含过滤
假如我们有个字符串列表,只想要那些包含".net"(不区分大小写)的。
List<string> technologies = new List<string> { "C#", ".NET", "Java", "dotnet", "JavaScript" };
List<string> netTechs = new List<string>();
foreach (var tech in technologies)
{
if (tech.ToLower().Contains(".net"))
netTechs.Add(tech);
}
foreach (var tech in netTechs)
{
Console.WriteLine(tech);
}
6. 过滤的特点和常见错误
过滤看着简单,其实也容易踩坑。我们来聊聊一些细节。
修改原始集合
如果你过滤完原始集合,然后又改了原集合——如果你已经新建了一个新列表,结果不会变。如果你只是保存了原列表的引用,原集合变了,结果也会跟着变。
List<int> filtered = new List<int>();
foreach (int n in numbers)
{
if (n > 2)
filtered.Add(n);
}
// 改一下原始列表
numbers.Add(10);
foreach (var n in filtered)
{
Console.WriteLine(n); // 10不会出现在这里
}
手动过滤的结果
手动过滤的结果通常是个新的可变列表(比如List<T>),你可以随便加、删元素啥的。
过滤和性能
循环里的过滤器会对每个元素都调用,如果条件里的函数很重——要小心。有时候直接写个foreach循环更简单,尤其是逻辑复杂、还要日志或者别的操作的时候。
GO TO FULL VERSION