我应该在什么时候使用它们的函数
我似乎无法绕过他们。
据我所知,它是动态地向类添加逻辑。 框架内的课程是否为此准备好了?
我为什么要扩展类并在扩展中添加它的function。 我将在全球范围内访问,并且更容易维护。
我读过有4种函子类型:
比较器
关闭
谓语
变压器
我们应该处理它们中的每一个。
ps在vb中有类似的东西吗?
所以我可以说我认为lambda表达式是仿函数。 这为我清理了一些东西:)(呵呵)
- Lambda表达式是算子吗?
- 匿名函数是函子吗?
但我问了这个问题,因为我遇到了另一种类型的fucntors,即这些:
delegate void FunctorDelegate(int value); class Addition { FunctorDelegate _delegate; public Addition AddDelegate(FunctorDelegate deleg) { _delegate += deleg; return this; } public int AddAllElements(IList list) { int runningTotal = 0; foreach( int value in list) { runningTotal += value; _delegate(value); } return runningTotal; } }
然后用它调用它:
int runningTotal = new Addition() .AddDelegate(new FunctorDelegate( delegate(int value) { if ((value % 2) == 1) { runningOddTotal += value; } })) .AddDelegate(new FunctorDelegate( delegate(int value) { if ((value % 2) == 0) { runningEvenTotal += value; } })) .AddAllElements(list);
所以没有花哨的lambda风格的东西。
现在我有了这个例子,但是为什么这是一个“好”的解决方案并不清楚。
作为程序员的快捷方式,代理人(仿函数)是否在大多数情况下用作lambda表达式或匿名方法? 据我所知,只有少数情况下他们实际上是问题的首选。
我认为你混淆了不同语言的术语。 您似乎在C ++或Java意义上使用“Functor”,例如, 请参阅维基百科页面 。 在C ++中,它是一个类的对象,它重载了函数调用操作符,因此它可以用作函数但是具有状态。
这在逻辑上与绑定到C#(或任何.NET语言)中的实例方法的委托相同。
写这样的东西有三种方法。 首先,您可以编写一个普通方法,然后将该方法的名称分配给委托变量。
void MyMethod() { Console.WriteLine("Hi!"); } void Foo() { Action a = MyMethod; a(); }
其次,您可以使用C#2.0中引入的匿名方法语法:
void Foo() { Action a = delegate { Console.WriteLine("Hi!"); } a(); }
第三,您可以使用C#3.0中引入的lambda语法:
void Foo() { Action a = () => Console.WriteLine("Hi!"); a(); }
最后两个的优点是方法的主体可以在包含方法中读取和写入局部变量。
lambda语法优于anon方法的优点是它更简洁,并且它对参数进行类型推断。
更新: anon-methods( delegate
关键字)优于lambdas的优点是,如果您不需要它们,您可以完全省略参数:
// correct way using lambda button.Click += (sender, eventArgs) => MessageBox.Show("Clicked!"); // compile error - wrong number of arguments button.Click += () => MessageBox.Show("Clicked!"); // anon method, omitting arguments, works fine button.Click += delegate { MessageBox.Show("Clicked!"); };
我只知道一个值得了解的情况,即在初始化事件时,您不必在触发之前检查null
:
event EventHandler Birthday = delegate { };
在其他地方避免很多废话。
最后,你提到有四种仿函数。 事实上,有一些无限可能的委托类型,虽然有些作者可能有他们的最爱,显然会有一些共同的模式。 Action
或Command
接受任何参数并返回void
,谓词接受某种类型的实例并返回true
或false
。
在C#3.0中,您可以使用您喜欢的任何类型的最多四个参数来创建一个委托:
Func f; // takes a string and an in, returns a double
回复:更新问题
你问(我认为)是否有很多lambda用例。 有可能超过列出!
您最常见的是在序列上运行的较大表达式(即时计算列表)中间。 假设我有一份人员名单,我想要一份四十岁的人员名单:
var exactlyForty = people.Where(person => person.Age == 40);
Where
方法是IEnumerable
接口上的扩展方法,其中T
在某种情况下是某种Person
类。
这在.NET中称为“Linq to Objects”,但在其他地方已知为序列或流上的纯函数式编程或“惰性”列表(同一事物的所有不同名称)。
在.NET术语中,我认为您所描述的是Delegate
– 它存在于所有.NET中,而不仅仅是C#。
我不确定“闭包”是否与“比较器/谓词/变换器”中的“类型”相同,因为在C#术语中,闭包只是一个实现细节,但可以是这三者中的任何一个。
在.NET中,委托以两种主要方式使用:
- 作为事件机制
- 提供function风格的编程
第一个很重要,但听起来你对第二个更感兴趣。 实际上,它们的运行方式与单方法接口非常相似……考虑:
List vals = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; List evenVals = vals.FindAll(i => i % 2 == 0); // predicate List valsAsStrings = vals.ConvertAll(i => i.ToString()); // transformer // sort descending vals.Sort((x, y) => y.CompareTo(x)); // comparer
在我们将代理外部的额外范围带入委托时,闭包更多:
int max = int.Parse(Console.ReadLine()); // perhaps 6 List limited = vals.FindAll(i => i <= max);
这里max
被捕获到委托中作为闭包。
重新说明“框架内的课程是否为此做好了准备?” - 很多都是,而且LINQ还有很长的路要走。 LINQ提供了(例如)所有IEnumerable
扩展方法 - 这意味着没有基于委托的访问的集合可以免费获取它们:
int[] data = { 1,2,3,4,5,6,7,8,9 }; var oddData = data.Where( i => i % 2 == 1 ); var descending = data.OrderBy(i => -i); var asStrings = data.Select(i => i.ToString());
这里的Where
和OrderBy
方法是采用委托的LINQ扩展方法。
有趣的术语; 我对“Functor”一词的自发解释是它提到了匿名方法。 这将是我对它的看法。
这些是我的一些典型用途:
比较(通常用于排序列表):
List ints = new List (); ints.AddRange(new int[] { 9, 5, 7, 4, 3, 5, 3 }); ints.Sort(new Comparison (delegate(int x, int y) { return x.CompareTo(y); })); // yes I am aware the ints.Sort() would yield the same result, but hey, it's just // a conceptual code sample ;o) // and the shorter .NET 3.5 version: ints.Sort((x, y) => { return x.CompareTo(y); });
我将使用这种方法进行比较,而不是在它自己的方法中使用该方法的委托,在这种特定排序仅在一个地方发生的情况下。 如果我可能想在其他地方使用相同的比较,那么它就会生活在自己的可重用方法中。
我的另一个常见用途是在unit testing中,当测试依赖于某些事件被引发时。 我发现在Workflow Foundation中unit testing工作流时必不可少:
WorkflowRuntime runtime = WorkflowHost.Runtime; WorkflowInstance instance = runtime.CreateWorkflow(typeof(CreateFile)); EventHandler WorkflowIdledHandler = delegate(object sender, WorkflowEventArgs e) { // get the ICreateFileService instance from the runtime ISomeWorkflowService service = WorkflowHost.Runtime.GetService(); // set the desired file content service.DoSomeWork(instance.InstanceId, inputData); }; // attach event handler runtime.WorkflowIdled += WorkflowIdledHandler; instance.Start(); // perform the test, and then detach the event handler runtime.WorkflowIdled -= WorkflowIdledHandler;
在这种情况下,将事件处理程序声明为匿名方法更简单,因为它使用在unit testing方法范围中定义的instance
变量。 如果我intstead选择将事件处理程序作为自己独立的方法来实现,我还需要找到一种方法来获取instance
,可能是通过引入一个类级别的成员,这在unit testing中看起来不是一个完美的设计类。
在我的代码中有更多的情况我会发现它,但它们通常有一两个共同点:
- 我没有兴趣从那个地方引用那段代码而不是那个特定的地方 。
- 该方法需要访问超出常规方法范围的数据
真正的答案是仿函数是一种数学对象,并且以不同的方式被不同语言“神化”。 例如,假设您有一个“容器”对象,它存储了一堆相同类型的其他对象。 (例如,一个集合或数组)然后,如果您的语言有一个让您“映射”容器的方法,那么您可以在容器中的每个对象上调用一个方法,那么容器就是一个仿函数 。
换句话说,仿函数是一个容器,其中包含一个方法,允许您将方法传递给其包含的东西。
每种语言都有自己的方式,有时会混淆使用。 例如,C ++使用函数指针来表示方法的“传入”,并将函数指针称为“仿函数”。 代理只是您可以传入的方法的句柄。您使用的术语“不正确”,就像C ++一样。
Haskell做对了。 您声明一个类型实现了functor接口,然后您获得了映射方法。
函数(如lambdas)也是函子,但是很难将函数看作“容器”。 简而言之,函数是返回值周围的“容器”,以这样的方式构造,即返回值(可能)取决于函数的参数。
我相信你的意思是Lambda表达式。 这些是您可以非常快速地编写的小函数,它们具有特征“=>”运算符。 这些是C#3.0的新function。
这个例子将是一个经典的变形金刚; 使用一个我们需要一个委托来定义Lambda函数的签名。
delegate int Transformer(int i);
现在使用此委托声明Lambda:
Transformer sqr = x => x * x;
我们可以像普通函数一样使用它:
Console.WriteLine(sqr(3)); //9
它们在LINQ查询中使用很多,例如Sort(Comparer),Search through(Predicate)。
“C#袖珍参考”一书(除了在我看来是最好的,在Lambdas上有很好的作用。(ISBN 978-0-596-51922-3)