13

我只是似乎无法绕过他们。

据我了解,它正在为类动态添加逻辑。框架内的类是否为此做好了准备?

为什么我应该扩展类并在扩展中添加功能。我将可以在全球范围内访问并且 afaik 更容易维护。

我读过有 4 种仿函数类型:

比较器闭

谓词
转换 器

我们可能应该处理它们中的每一个。

ps 在vb中有类似的东西吗?

所以我可以说我认为 lambda 表达式是函子。这让我明白了一点:)(呵呵)

  • Lambda 表达式是函子吗?
  • 匿名函数是函子吗?

但我问了这个问题,因为我遇到了另一种类型的 fucntor,即这些:

delegate void FunctorDelegate(int value);
class Addition {
    FunctorDelegate _delegate;

    public Addition AddDelegate(FunctorDelegate deleg) {
        _delegate += deleg;
        return this;
    }
    public int AddAllElements(IList< int> 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 表达式或匿名方法,只是作为程序员的捷径?据我所知,只有少数情况下它们实际上是解决问题的首选。

4

5 回答 5

23

我认为您混淆了不同语言的术语。您似乎在使用 C++ 或 Java 意义上的“Functor”,例如参见 wikipedia page。在 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-methods 的优点是它更简洁,并且可以对参数进行类型推断。

更新:与 lambdas相比,anon-methods ( delegatekeyword) 的优点是,如果不需要参数,可以完全省略它们:

// 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 { };

在其他地方避免了很多废话。

最后,您提到有四种仿函数。事实上,可能的委托类型有无数种,尽管有些作者可能有他们的最爱,而且显然会有一些常见的模式。Actionor不Command带参数并返回void,谓词带某种类型的实例并返回trueor false

在 C# 3.0 中,您可以使用最多四个您喜欢的任何类型的参数来创建一个委托:

Func<string, int, double> f;  // takes a string and an in, returns a double

回复:更新的问题

你问(我认为)lambdas 是否有很多用例。有比可能列出的更多!

您最常在对序列(动态计算的列表)进行操作的较大表达式的中间看到它们。假设我有一份人员名单,我想要一份正好是 40 岁的人员名单:

var exactlyForty = people.Where(person => person.Age == 40);

Where方法是IEnumerable<T>接口上的扩展方法,T在这种情况下是某种Person类。

这在 .NET 中称为“Linq to Objects”,但在其他地方称为对序列或流或“惰性”列表的纯函数式编程(同一事物的所有不同名称)。

于 2009-06-10T09:51:38.207 回答
7

在 .NET 术语中,我认为您所描述的是Delegate- 它存在于所有 .NET 中,而不仅仅是 C#。

我不确定“闭包”是否会成为与比较器/谓词/转换器相同的“类型”,因为在 C# 术语中,闭包只是一个实现细节,但可以这三个中的任何一个。

在 .NET 中,委托以两种主要方式使用:

  • 作为事件机制
  • 提供函数式编程

第一个很重要,但听起来您对第二个更感兴趣。实际上,它们的操作很像单方法接口......考虑:

List<int> vals = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> evenVals = vals.FindAll(i => i % 2 == 0); // predicate
List<string> 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<int> limited = vals.FindAll(i => i <= max);

在这里,max作为闭包被捕获到委托中。

重新“框架内的类是否为此做好了准备?” - 很多,而且 LINQ 在允许这一点更广泛方面还有很长的路要走。LINQ 提供了(例如)所有的扩展方法IEnumerable<T>——这意味着没有基于委托的访问的集合可以免费获取它们:

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());

这里的WhereandOrderBy方法是接受委托的 LINQ 扩展方法。

于 2009-06-10T07:57:21.497 回答
1

有趣的术语;我对“函子”一词的自发解释是它指的是匿名方法。所以这将是我的看法。

这些是我的一些典型用途:

比较(通常用于排序列表):

List<int> ints = new List<int>();
ints.AddRange(new int[] { 9, 5, 7, 4, 3, 5, 3 });
ints.Sort(new Comparison<int>(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);
});

我将使用这种方法进行比较,而不是在它自己的方法中使用该方法的委托,在这种特定排序仅发生在一个地方的情况下。如果我可能想在其他地方使用相同的比较,它就会以自己的可重用方法存在。

我的另一个相当常见的用途是在单元测试中,当测试依赖于某个被引发的事件时。我发现在 Workflow Foundation 中对工作流进行单元测试时,这是必不可少的:

WorkflowRuntime runtime = WorkflowHost.Runtime;  
WorkflowInstance instance = runtime.CreateWorkflow(typeof(CreateFile)); 
EventHandler<WorkflowEventArgs> WorkflowIdledHandler = delegate(object sender, WorkflowEventArgs e)
{
    // get the ICreateFileService instance from the runtime  
    ISomeWorkflowService service = WorkflowHost.Runtime.GetService<ISomeWorkflowService>();

    // 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; 

在这种情况下,将事件处理程序声明为匿名方法会更简单,因为它使用instance在单元测试方法范围中定义的变量。如果我选择将事件处理程序作为它自己的单独方法来实现,我还需要找到一种方法让它接受instance,可能是通过引入一个类级别的成员,这在单元测试类中似乎不是一个完美的设计.

在更多情况下,我在我的代码中发现了这一点,但它们通常有一两个共同点:

  • 我没有兴趣从那个特定地方以外的任何地方引用那段代码
  • 该方法需要访问超出常规方法范围的数据
于 2009-06-10T08:56:55.213 回答
1

真正的答案是函子是一种数学对象,并且被不同的语言以不同的方式“具体化”。例如,假设您有一个“容器”对象,它存储了一堆相同类型的其他对象。(例如,集合或数组)然后,如果您的语言有一个方法可以让您“映射”容器,以便您可以在容器中的每个对象上调用方法,那么容器将是一个 functor

换句话说,函子是一个容器,它有一个方法,可以让你将一个方法传递给它所包含的东西。

每种语言都有自己的方式来做到这一点,有时它们会将用法混为一谈。例如,C++ 使用函数指针来表示方法的“传入”,并将函数指针称为“函子”。代表只是您可以传入的方法的句柄。您正在“错误地”使用术语,就像 C++ 一样。

Haskell 做对了。你声明一个类型实现函子接口,然后你得到映射方法。

Functions (like lambdas) are functors too, but it can be kind of hard to think of a function as a "container". In short, a function is a "container" around a return value, constructed in such a way that the return value (possibly) depends on the function's arguments.

于 2015-11-24T23:27:53.133 回答
0

我确定您的意思是 Lambda 表达式。这些是您可以非常快速地编写的小函数,并且它们具有特征“=>”运算符。这些是 C# 3.0 的新特性。

这个例子将是一个经典的 Transformer;要使用一个,我们需要一个委托来定义 Lambda 函数的签名。

delegate int Transformer(int i);

现在用这个委托声明一个 Lambda:

Transformer sqr = x => x * x;

我们可以像使用普通函数一样使用它:

Console.WriteLine(sqr(3)); //9

这些在 LINQ 查询中被大量使用,例如排序(比较器)、搜索(谓词)。

这本书“C#袖珍参考”(除了我认为最好的之外,在 Lambdas 上有很好的部分。(ISBN 978-0-596-51922-3)

于 2009-06-10T08:06:15.753 回答