7

有一个名为的类Student具有属性Id,NamePhone。在 UI 表单中Student,有以下方式的列表:

List<Student> students=new List<Student>();

最后有一个dataGridview_Cellclick事件代码,使用以下代码:

string id = dataGridview.Rows[e.RownIndex].Cells[0].Value.ToString();  
Student aStudent = students.Find(i=> i.Id== id);

做什么的students.Find(i=> i.Id== id)?这是什么意思?=>符号的含义是什么?它是如何工作的?

4

6 回答 6

4

它们被称为 Lambda 表达式...Lambda 表达式使用特殊的语法。它们允许将函数用作变量或字段等数据。lambda 表达式语法使用 => 运算符。这将匿名函数的参数和语句体分开。

您可以将其称为“Goes to”。

=> 运算符可以读作“goes to”,它总是在声明 lambda 表达式时使用。lambda 表达式允许您使用带有可执行语句的函数作为参数、变量或字段。

请参阅 MSDN 上的此链接以更好地理解它。

于 2012-12-01T09:25:17.843 回答
3

这是一个goes to运算符(或lambda 运算符),它在lambda 表达式(创建匿名方法)中用于将输入变量与 lambda 主体分开。

在您的示例students.Find(i => i.Id== id)输入变量i转到 lambda 主体i.Id == id(即作为匿名方法参数传递)。


另请查看List<T>.Find您正在使用的方法。它接受类型的谓词T,在您的情况下是Predicate<Student>。Predicated 是一个委托,它表示定义一组标准并确定指定对象是否满足这些标准的方法。它有以下签名:

public delegate bool Predicate<in Student>(Student obj)

因此,您需要传递一个方法,该方法接受一个学生对象并返回一个布尔值。您可以为此创建正常的命名方法:

private bool IsStudentHasIdEqualTo5(Student s)
{
   return s.Id == 5;
}

并以这种方式使用它:

Student aStudent = students.Find(IsStudentHasIdEqualTo5);

但是您需要验证不同的 id 值。有两个选项 - 在您的类中创建字段,这将在学生谓词方法中可用,或者创建类,它将具有此方法和字段:

class StudentSearcher
{
    private int _id; // capture id

    public StudentSearcher(int id)
    {
        _id = id;
    }

    // method has same signature as bool Predicate(Student obj)
    public bool VerfyId(Student s)
    {
        return s.Id == _id;
    }
}

现在您可以使用这个命名方法并id为学生验证提供不同的值:

var searcher = new StudentSearcher(id);
Student aStudent = students.Find(searcher.VerfyId);

但是为每次搜索创建这样的方法和类并不是很有效。这就是我们有委托(和 lambdas)的原因。无需声明新的命名方法,您可以在需要的地方创建没有名称(匿名)的方法,编译器将为您生成通常的命名方法:

Student aStudent = students.Find(delegate(Student s) { 
                                      return s.Id == id; 
                                 });

完全相同的代码可以用 lambda 语法编写(省略了委托关键字,推断参数类型,goes to用于分隔参数和方法体的运算符,也省略了返回关键字):

Student aStudent = students.Find(s => s.Id == id);

这里的神奇之处在于编译器将在幕后生成如上图所示的类。该类将具有带有谓词签名的方法,并且还将具有用于捕获的字段以id进行搜索。

于 2012-12-01T09:25:16.897 回答
1
  • 做什么students.Find(i=> i.Id== id)

情况是这样的。您有一个 Student 对象列表和您感兴趣的学生的 id。如果 Student 对象存储在您自己定义的集合中,该集合有一个 Find 方法,该方法接受一个 id 并返回具有该 id 的学生,您的代码看起来像这样:

Student aStudent = students.Find(id);

但是,当 Microsoft 定义通用 List 集合时,他们不可能知道如何使用它——而且他们也不想知道。他们想让您有机会将它与学生对象或您能想到的任何其他类型一起使用。但这意味着他们必须通过提供只有您知道的有关您的问题的信息,为您提供一种方法来找到您所追求的元素。在这个特定的实例中,您知道您正在寻找存储在学生列表中的 Student 对象,该对象的 Id 字段与您拥有的 id 匹配。如果我给你一个对象,让我们称之为它i,你可以通过执行以下检查来告诉我它是否是你正在寻找的对象:

i.Id == id 

如果我给您一个名为 的对象student,您将能够执行测试,您将能够通过执行测试student来告诉我它是否是您所追求的对象

学生.Id == id

(如果您没有 id 而是其他一些唯一确定 Student 对象(即键)的信息,您可能希望指定不同的条件。灵活性很重要!)

所以,现在我们进入下一个问题。

  • 这是什么意思?

    让我们创建一个约定。您将确定要调用的学生对象,并提供适当的检查来选择您所追求的学生。您将代表该学生的标识符放在左侧,=>将检查条件放在右侧。因此你可以有类似的东西:

    学生 => student.Id == id

或者如果您更喜欢参考讨论中的学生对象i而不是studentthen 您可以写

i => i.Id == id

现在看看。这是什么?给定一个 Student 对象返回一个布尔值。这是一个功能!

  • => 符号的含义是什么?

    它是一个定义函数的运算符。论点在左侧,主体在右侧。

  • 它是如何工作的?

    假设您希望我编写一个函数,该函数接受一个 List 参数和一个函数,例如我们正在讨论的函数,并在不使用 Find 方法的情况下返回匹配的学生。我可能会写这样的东西。

    Student FindStudent(List students, Func match) { foreach(Student st in students) if(match(st)) return st; 返回圣;}

    您可能会发现一切都非常熟悉,除了我猜想的 Func 类型,您可以想象它是从 Student 到布尔值的函数类型。我也可以使用谓词,你可以猜到它是学生谓词的一种类型。

    但是,您不需要我写这个或自己写这个,因为微软已经为我们做了这件事。Find()Microsoft为该类编写的代码List<T>与上面的非常相似。

希望有帮助!


这应该涵盖您所要求的所有内容。但要获得更广泛的图片,您可能还想看看:


一些可能对您有所帮助的离别说明:

  • Lambda 表达式可能有多个参数,例如 `(x,y) => x*x + y + 43'
  • lambda 表达式的右侧可能是一个代码块,例如 `(x,y) => {return x*x + y + 43;}'
  • 带有字符串和返回布尔值的 int 参数的 lambda 表达式的类型是 `Func'
  • 带有字符串参数和不返回任何内容的 int 参数的 lambda 表达式的类型是“Action”
于 2012-12-01T10:10:28.580 回答
1

=>goto运算符,此表达式是lambda表达式

msdn

于 2012-12-01T09:28:57.883 回答
1

lambda 运算符将函数参数与其主体分开。

(arg1,arg2...argn)
=>
{
  //body
}

身体也可以没有括号..但它仍然是一个“身体”。

(arg1,arg2..argn) => 1 ;

Student aStudent = students.Find(i=> i.Id== id);

Find 是一个采用 lambda 表达式的 Linq 方法。

它将贯穿学生的每个元素。

该元素由i- 尽管student更有意义 - 表示,并被传递到“body”中。如果i.Id==idFind 方法返回student元素。

于 2012-12-01T09:30:31.863 回答
0

List<T>定义一个方法Find(Predicate<T> match)

Predicate<T>是一个委托,可以引用与其签名匹配的任何方法

bool Predicate(T param)

在您的情况下,您正在Find学生列表上调用该方法,List<Student>因此该Find方法需要一个与以下签名匹配的函数:

bool MyFindMethod(Student param)

您可以在您的类中定义这样的方法,如下所示:

bool MyFindMethod(Student param)
{
    // Logic goes here
}

并将其传递给您的Find方法,如下所示:

students.Find(MyFindMethod)

您使用的方法小而简单,因此在类中创建方法的开销不值得,因此 lambda 表达式允许您以非常简洁的方式定义相同的方法。

s => s.Id == id

相当于:

bool AnonymousMethod(Student param)
{
    return s.Id == id;
}

运算符左侧的=>项是传递给方法的参数,运算符右侧的项=>是方法体。

请注意,编译器足够聪明,可以确定参数(s在我的示例中)是类型Student,因此不需要指定。

如果您有其他类型的列表 EG

public class Customer
{
    public string Name { get; set;}
}

public IList<Customer> customers = new List<Customer>();

然后编译器会推断该参数是类型Customer而不是学生。

customers.Find(c => c.Name == name);

请注意,参数可以任意命名,但通常保持为单个字母以保持表达式简洁。

如果您了解所有这些,那么您将看到您的代码

students.Find(i => i.Id == id)

基本上是调用一个以 aStudent作为参数的方法并对其进行评估以查看它是否与运算符右侧的条件匹配=>。如果参数符合条件(即,如果学生Idid变量匹配),则表达式将返回 true。这告诉Find方法它找到了一个匹配项,并且这个对象将被返回。

我在这里回答了一个与WPF 相关的类似问题,但不同上下文中的示例可能有助于您的理解。

于 2012-12-01T10:29:39.237 回答