简体   繁体   English

C#扩展方法和F#管道转发操作符之间有什么关系?

[英]What is the relation between C# extension methods and the F# pipe-forward operator?

After using F# for a few small problems, I've found it helpful for myself to think of C# extension methods as 'a way of turning the . 在使用F#解决一些小问题之后,我发现将C#扩展方法看作是一种转变方式对我自己很有帮助。 into a pipe-forward operator'. 进入管道运营商'。

For example, given a sequence of Int32s named ints, the C# code: 例如,给定一个名为int的Int32s序列,C#代码:

ints.Where(i => i > 0)
    .Select(i => i * i)

is similar to the F# code 类似于F#代码

let where = Seq.filter
let select = Seq.map

ints |> where (fun i -> i > 0)
     |> select (fun i -> i * i)

In fact, I often think of the extension methods on IEnumerable as simply a library of functions that provide similar functionality to F#'s Seq module. 事实上,我经常认为IEnumerable上的扩展方法只是一个函数库,它提供与F#的Seq模块类似的功能。

Obviously the piped parameter is the last parameter in an F# function, but the first parameter in a C# extension method - but apart from that, are there any issues with using that explanation when describing extension methods or pipe-forward to other developers? 显然,管道参数是F#函数中的最后一个参数,但是C#扩展方法中的第一个参数 - 但除此之外,在描述扩展方法或管道转发给其他开发人员时是否存在使用该解释的问题?

Would I be misleading them, or is it a helpful analogy? 我会误导他们,还是一个有用的类比?

I wouldn't make that analogy because an extension method can be called as if it were an instance method whereas piping is clearly syntactically quite different. 我不会进行类比,因为可以调用扩展方法,就好像它是一个实例方法,而管道在语法上显然是完全不同的。 Additionally, F# also has extension methods (as well as extension properties and events!), so I think there's a real possibility of causing confusion. 另外,F#还有扩展方法(以及扩展属性和事件!),所以我认为真的有可能引起混淆。

However, I think that it is true that both the piping style and extension methods allow computations to be described fluently as a set of steps, which is probably the similarity that you are driving at. 但是,我认为管道样式和扩展方法都可以将计算流程描述为一组步骤,这可能与您正在推动的相似性。 Conceptually, it is nice have the code reflect the idea of "take this set of ints, keep only the subset satisfying i>0, and then square each of those", which is how the task might be described in English. 从概念上讲,如果代码反映了“采用这组内容,只保留满足i> 0的子集,然后将每个中的每一个都对齐”的想法,这很好,这就是用英语描述任务的方式。 Both the piping syntax and C# extension methods allow this sort of thing. 管道语法和C#扩展方法都允许这种事情。

I also think that this is a very useful analogy. 我也认为这是一个非常有用的比喻。 In fact, I used exactly this analogy when describing the pipelining operator in my Real World Functional Programming book (which tries to explain functional ideas to people with C# background). 实际上,在我的“ 真实世界功能编程”一书中描述流水线操作符时,我使用了这个类比(它试图向具有C#背景的人解释功能性想法)。 Below is a quote from Chapter 6. 以下是第6章的引用。

Regarding the differences between the two - there are some conceptual differences (eg extension methods "add members to objects"), but I don't think this has any impact on the way we use them in practice. 关于两者之间的差异 - 存在一些概念上的差异(例如扩展方法“将成员添加到对象”),但我认为这对我们在实践中使用它们的方式没有任何影响。

  • One notable practical difference between C# extension methods and F# functions is editor support - when you type "." C#扩展方法和F#函数之间的一个值得注意的实际区别是编辑器支持 - 当您键入“。”时。 in C#, you can see extension methods for the particular type. 在C#中,您可以看到特定类型的扩展方法。 I believe that F# IntelliSense could show a filtered list when you type |> (in principle) as well, but it is probably much more work and it isn't supported yet. 我相信当您键入|> (原则上)时,F#IntelliSense可以显示已过滤的列表,但它可能更多的工作,并且它还不支持。

  • Both of the constructs are used to enable expression-based compositional programming style. 这两个构造都用于启用基于表达式的组合编程风格。 By this I mean that you can write much larger portions of code as a single expression that describes what should be done (without extension methods/pipelining operator, you would probably break code into multiple stements). 通过这个我的意思是你可以编写更大的代码部分作为描述应该做什么的单个表达式(没有扩展方法/流水线操作符,你可能会将代码分成多个步骤)。 I think that this style of programming generally leads to a more declarative code. 我认为这种编程风格通常会导致更具说明性的代码。


The pipelining operator ( |> ) allows us to write the first argument for a function on the left side; 流水线操作符( |> )允许我们在左侧写入函数的第一个参数; that is, before the function name itself. 也就是说,在函数名称之前。 This is useful if we want to invoke a several processing functions on some value in sequence and we want to write the value that's being processed first. 如果我们想要按顺序在某个值上调用多个处理函数并且我们想要先写入正在处理的值,这非常有用。 Let's look at an example, showing how to reverse a list in F# and then take its first element: 让我们看一个例子,展示如何在F#中反转列表然后获取它的第一个元素:

 List.hd(List.rev [1 .. 5]) 

This isn't very elegant, because the operations are written in opposite order then in which they are performed and the value that is being processed is on the right side, surrounded by several braces. 这不是很优雅,因为操作以相反的顺序写入然后执行它们并且正在处理的值在右侧,由几个括号围绕。 Using extension methods in C#, we'd write: 在C#中使用扩展方法,我们写道:

 list.Reverse().Head(); 

In F#, we can get the same result by using the pipelining operator: 在F#中,我们可以通过使用流水线操作符获得相同的结果:

 [1 .. 5] |> List.rev |> List.hd 

Even though, this may look tricky, the operator is in fact very simple. 即使这看起来很棘手,但操作员实际上非常简单。 It has two arguments - the second one (on the right side) is a function and the first one (on the left side) is a value. 它有两个参数 - 第二个(在右侧)是一个函数,第一个(在左侧)是一个值。 The operator gives the value as an argument to the function and returns the result. 运算符将值作为参数提供给函数并返回结果。

In some senses, pipelining is similar to calling methods using dot-notation on an object, but it isn't limited to intrinsic methods of an object. 在某些意义上,流水线操作类似于在对象上使用点符号调用方法,但它不限于对象的内部方法。 This is similar to extension methods, so when we write a C# alternative of an F# function that's usually used with the pipelining operator, we'll implement it as an extension method. 这与扩展方法类似,因此当我们编写通常与流水线操作符一起使用的F#函数的C#替代时,我们将其实现为扩展方法。

You may also want to look at the reverse function composition operator if you wish to reuse these pipelines. 如果您希望重用这些管道,您可能还需要查看反向函数组合运算符。

let where = Seq.filter
let select = Seq.map
let whereGreaterThanOneComputeSquare = 
   where (fun i -> i > 0) << select (fun i -> i * i)
ints |> whereGreaterThanOneComputeSquare

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM