CodeGym /课程 /C# SELF /元素过滤

元素过滤

C# SELF
第 29 级, 课程 0
可用

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循环更简单,尤其是逻辑复杂、还要日志或者别的操作的时候。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION