C#谓词教程展示了如何在C#中使用谓词。使用谓词,我们可以创建更简洁和可读性更高的代码。
谓词
Predicate在一般意义上是关于某事的陈述,要么是真的,要么是假的。在编程中,谓词表示返回布尔值的单参数函数。
C#谓词
C#中的谓词是通过委托实现的。Predicate委托表示定义一组条件并确定指定对象是否满足这些条件的方法。
C#谓词示例
以下示例创建一个简单的C#谓词。
var data = new List<int> { 1, -2, 3, 0, 2, -1 };
var predicate = new Predicate<int>(isPositive);
var filtered = data.FindAll(predicate);
Console.WriteLine(string.Join(",", filtered));
bool isPositive(int val)
{
return val > 0;
}
在示例中,谓词用于过滤掉正值。
var predicate = new Predicate<int>(IsPositive);
谓词委托已定义;它采用IsPositive方法作为参数。
var filtered = data.FindAll(predicate);
我们将谓词传递给列表的FindAll方法,该方法检索谓词返回true的所有值。
bool IsPositive(int val)
{
return val > 0;
}
对于所有大于零的值,IsPositive返回真。
$ dotnet run 1,3,2
使用匿名方法的C#谓词
以下示例将匿名方法传递给委托。
var data = new List<int> { 1, -2, 3, 0, 2, -1 };
Predicate<int> isPositive = delegate(int val) { return val > 0; };
var filtered = data.FindAll(isPositive);
Console.WriteLine(string.Join(",", filtered));
该示例使用delegate关键字来定义匿名方法。
使用lambda表达式的C#谓词
C#lambda表达式简化了C#谓词的创建。Lambda表达式是使用=>lambda声明运算符创建的。
var words = new List<string> { "falcon", "wood", "tree",
"rock", "cloud", "rain" };
Predicate<string> hasFourChars = word => word.Length == 4;
var words2 = words.FindAll(hasFourChars);
Console.WriteLine(string.Join(',', words2));
在示例中,我们找出所有包含四个字母的单词。
$ dotnet run wood,tree,rock,rain
带有Exists的C#谓词
列表的Exists方法确定列表是否包含与指定谓词定义的条件匹配的元素。
var words = new List<string> { "sky", "", "club", "spy", "silk", "summer",
"war", "cup", "cloud", "coin", "small", "terse", "",
"snow", "snail", "see"};
Predicate<string> pred = String.IsNullOrEmpty;
if (words.Exists(pred))
{
Console.WriteLine("There is an empty string");
}
else
{
Console.WriteLine("There is no empty string");
}
我们检查列表中是否有一些空字符串。
$ dotnet run There is an empty string
带有RemoveAll的C#谓词
列表的RemoveAll方法删除所有符合指定谓词定义的条件的元素。
var words = new List<string> { "sky", "town", "club", "spy", "silk", "snail",
"war", "cup", "cloud", "coin", "small", "terse"};
Predicate<string> HasThreeChars = word => word.Length == 3;
words.RemoveAll(HasThreeChars);
Console.WriteLine(string.Join(", ", words));
我们有一个单词列表。我们删除所有包含三个拉丁字符的单词。
$ dotnet run town, club, silk, snail, cloud, coin, small, terse
C#谓词多个条件
下一个示例使用具有两个条件的谓词。
var countries = new List<Country>
{
new ("Iran", 80840713),
new ("Hungary", 9845000),
new ("Poland", 38485000),
new ("India", 1342512000),
new ("Latvia", 1978000),
new ("Vietnam", 95261000),
new ("Sweden", 9967000),
new ("Iceland", 337600),
new ("Israel", 8622000)
};
Predicate<Country> p1 = c => c.Name.StartsWith("I");
Predicate<Country> p2 = c => c.Population > 1000_0000;
Predicate<Country> CombineAnd = c => (p1(c) && p2(c));
var result = countries.FindAll(CombineAnd);
Console.WriteLine(string.Join(", ", result));
record Country(string Name, int Population);
我们创建了一个国家列表。我们找到所有以“I”开头且人口超过一百万的国家/地区。
Predicate<Country> p1 = c => c.Name.StartsWith("I");
Predicate<Country> p2 = c => c.Population > 1000_0000;
我们定义了两个谓词。
Predicate<Country> CombineAnd = c => (p1(c) && p2(c));
我们结合这两个谓词。
var result = countries.FindAll(CombineAnd);
我们将组合谓词应用于FindAll方法。
$ dotnet run
Country { Name = Iran, Population = 80840713 }, Country { Name = India, ...
C#否定谓词
我们可以创建一个委托来否定已经定义的委托。
var words = new List<string> { "falcon", "wood", "tree",
"rock", "cloud", "rain" };
Predicate<string> HasFourChars = word => word.Length == 4;
Predicate<string> Negate = word => !HasFourChars(word);
var words2 = words.FindAll(Negate);
Console.WriteLine(string.Join(',', words2));
// Predicate<T> Negate<T>(Predicate<T> predicate)
// {
// return x => !predicate(x);
// }
该示例否定了HasFourChars委托。另一种解决方案已被注释掉。
$ dotnet run falcon,cloud
这些是长度不是四个字母的单词。
带有Func的C#谓词
Func是一个通用委托类型。它可以包含0到16个输入参数,并且必须有一个返回类型。Predicate是Func的特化。
var data = new List<Person>
{
new ("John Doe", "gardener"),
new ("Robert Brown", "programmer"),
new ("Lucia Smith", "teacher"),
new ("Thomas Neuwirth", "teacher")
};
ShowOutput(data, r => r.Occupation == "teacher");
void ShowOutput(List<Person> list, Func<Person, bool> condition)
{
var data = list.Where(condition);
foreach (var person in data)
{
Console.WriteLine($"{person.Name}, {person.Occupation}");
}
}
record Person(string Name, string Occupation);
该示例创建了一个人员列表。ShowOutput方法将谓词作为第二个参数。它返回所有教师。
带有Array.FindAll的C#谓词
Array.FindAll方法检索与指定谓词定义的条件匹配的所有元素。
User[] users =
{
new (1, "John", "London", "2001-04-01"),
new (2, "Lenny", "New York", "1997-12-11"),
new (3, "Andrew", "Boston", "1987-02-22"),
new (4, "Peter", "Prague", "1936-03-24"),
new (5, "Anna", "Bratislava", "1973-11-18"),
new (6, "Albert", "Bratislava", "1940-12-11"),
new (7, "Adam", "Trnava", "1983-12-01"),
new (8, "Robert", "Bratislava", "1935-05-15"),
new (9, "Robert", "Prague", "1998-03-14"),
};
var age = 60;
Predicate<User> olderThan = e => GetAge(e) > age;
var res = Array.FindAll(users, olderThan);
foreach (var e in res)
{
Console.WriteLine(e);
}
int GetAge(User user)
{
var dob = DateTime.Parse(user.DateOfBirth);
return (int) Math.Floor((DateTime.Now - dob).TotalDays / 365.25D);
}
record User(int id, string Name, string City, string DateOfBirth);
我们获取所有60岁以上的用户。
Predicate<User> olderThan = e => GetAge(e) > age;
在谓词定义中,我们使用GetAge方法来确定用户的年龄。
var res = Array.FindAll(users, olderThan);
Array.FindAll方法检索与指定谓词定义的条件匹配的所有元素。
int GetAge(User user)
{
var dob = DateTime.Parse(user.DateOfBirth);
return (int) Math.Floor((DateTime.Now - dob).TotalDays / 365.25D);
}
GetAge方法解析出生日期字符串并计算当前年龄。
$ dotnet run
User { id = 4, Name = Peter, City = Prague, DateOfBirth = 1936-03-24 }
User { id = 6, Name = Albert, City = Bratislava, DateOfBirth = 1940-12-11 }
User { id = 8, Name = Robert, City = Bratislava, DateOfBirth = 1935-05-15 }
C#泛型FindAll
接下来,我们定义一个FindAll列表扩展方法。
public static class ExtensionMethods
{
public static List<T> FindAll<T>(this List<T> vals, List<Predicate<T>> preds)
{
List<T> data = new List<T>();
foreach (T e in vals)
{
bool pass = true;
foreach (Predicate<T> p in preds)
{
if (!(p(e)))
{
pass = false;
break;
}
}
if (pass) data.Add(e);
}
return data;
}
}
FindAll方法返回填充所有指定谓词的列表元素。
public static List<T> FindAll<T>(this List<T> vals, List<Predicate<T>> preds)
FindAll方法将通用谓词函数列表作为参数。它返回过滤后的通用列表。
var preds = new List<Predicate<int>>();
preds.Add(e => e > 0);
preds.Add(e => e % 2 == 0);
var vals = new List<int> {-3, -2, -1, 0, 1, 2, 3, 4};
var filtered = vals.FindAll(preds);
foreach (var e in filtered) {
Console.WriteLine(e);
}
Console.WriteLine("---------------------");
var words = new List<string> {"sky", "wrath", "wet", "sun", "pick", "who",
"cloud", "war", "water", "jump", "ocean"};
var preds2 = new List<Predicate<string>>();
preds2.Add(e => e.StartsWith("w"));
preds2.Add(e => e.Length == 3);
var filtered2 = words.FindAll(preds2);
foreach (var e in filtered2) {
Console.WriteLine(e);
}
我们定义了两个列表:一个整数列表和一个字符串列表。从整数列表中,我们过滤掉所有正偶数。从字符串列表中,我们得到所有以’w’开头且具有三个字母的单词。
$ dotnet run 2 4 --------------------- wet who war
在本文中,我们使用了C#Predicate。
列出所有C#教程。
