F# - 传递给C#方法的参数 - 它们是元组还是什么? [英] F# - On the parameters passed to C# methods - are they tuples or what?

查看:128
本文介绍了F# - 传递给C#方法的参数 - 它们是元组还是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看过很多次,




从组件F#或其他任何.NET语言(几乎)没有区别产生的。




我然后用F#和.NET的4(测试版2)C#互操作试验。我创建了一个新的解决方案,和C#项目,下面的类:

 公共类MyClass的{
公共静态INT添加(INT A,INT b){返回A + b; }
}



然后,在F#项目,它引用了C#项目后,我试着

  MyClsas.Add(4,5)|> printfn%D//输出9(不开玩笑!)



到目前为止好。然后还有一句我已经看了很多遍(也许在不同的书籍)来到我的脑海:




在向函数传递参数从其他。 NET库,你使用类似语法.MethodName(PARM1,parm2),即,参数将作为一个元组通过。




补充一点的东西,我曾经在这里读SO(但无法找到链接),就一个问题,其中OP试图用一个像 [4创建, 5,6] (当他的意思 [4; 5; 6] ):



< BLOCKQUOTE>

逗号是'元组创造运营商,对一切使用分号。




然后我修改我的类为以下内容:

 公共类MyClass的{
公共静态INT添加(int类型的, INT b){返回A + b; }
公共静态INT添加(元组LT; INT,INT>一种){返回a.Item1; }
}

现在我试图把它用在F#:

  MyClass.Add(4,5)|> printf的%D//输出...(请继续阅读!)



因此,增加了以上三个报价,我们可以得出这样的结论:




  • 它认为,当F#将创建一个元组(4,5)

  • 然后,它会调用过载添加(元组LT; INT,INT>)

  • 因此,将打印4



要我吃惊的是,它印9 。是不是很有趣吗?



什么是真正发生在这里?以上报价,这实际观察似乎是矛盾的。你能证明F#的推理,也许指着一些MSDN文档如果可能的话?



谢谢!



编辑



(添加更多的信息(来自Blindy的答案))



如果你这样做:

  MyClass.Add((4,5))|> printfn%D//输出9 



F#调用添加(元组LT ; INT,INT>)超载



不过,如果你创建另一个F#项目(所以不同的装配)用这样的:

 命名空间MyFSharpNamespace 
型MyFShapClass =类
静态成员添加XY = X + Y

您可以使用它在C#这样

 公共静态无效的主要(字串[] args){
MyFSharpNamespace.MyFSharpClass.Add(4,5);
}



到目前为止好。现在,当您尝试从F#使用它(从另一个项目,另一个程序集),你要做的:

  MyFSharpNamespace。 MyFSharpClass.Add 4 5 |> printfn%d个

如果您传递的参数为(4,5 ) F#不能编译,因为添加 INT - > INT - > INT ,而不是(INT * INT) - GT; INT



这是怎么回事?!?


解决方案

在向函数传递参数从其他.NET库,你使用类似语法.MethodName(PARM1,parm2),即,参数将作为一个元组通过。




这是比这更可怕的。见方法重载两岸的描述从语言规范



它说什么,基本上是一个方法调用这样的说法是不是一个真正的元组。这是一个的语法的元组,这意味着一个逗号分隔的东西名单,但括号是方法调用语法的一部分,因此是逗号。这就是为什么,例如, OM(A = 1,B = 2)不是一个方法调用带有两个布尔值的元组,而是两个命名的参数。



所以,通常情况下,每一个逗号分隔的分量只是映射到一个独特的说法。因此,为什么添加(1,2)要求添加(INT,INT)超载和 ADD((1,2))通话添加(元组LT; INT,INT>)。 ,没有模棱两可



不过,对于您的特定情况下踢在特殊情况下是这样的:



<块引用>

如果没有指定的实际参数,并且在中号只有一个候选方法,只接受一个非可选参数,然后分解的 ARG 来元组的形式被忽略,有一个名为实际 ARG ARG 本身。




所以,当你删除了所有重载除了元组一听,顿时整个事情的括号内有效地视为一个电话一个元组构造函数。但是,如果你倒是例如有两个重载,添加(INT)添加(元组LT; INT,INT>),然后的一个电话表格添加(1,2)不会在所有的解决。


I've read many times that

Assemblies generated from F# or any other .NET language are (almost) indistinguishable.

I was then experimenting with F# and C# interop on .NET 4 (beta 2). I created a new solution, and a C# project, with the following class:

public class MyClass {
    public static int Add(int a, int b) { return a + b; }
}

Then, on a F# project, after referencing the C# project, I tried:

MyClsas.Add(4, 5) |> printfn "%d" // prints 9 (no kidding!)

So far so good. Then another sentence I've read many times (perhaps on different books) came to my mind:

When passing arguments to functions from other .NET libraries, you use a syntax like ".MethodName(parm1, parm2)", that is, the parameters are passed as a Tuple.

Add that to something that I've once read here on SO (but wasn't able find it to link to), on a question where the OP was trying to create a using like [ 4, 5, 6 ] (when he meant [4; 5; 6]):

"Comma is the 'tuple creating operator', for everything else use semi-colon."

Then I modified my class to the following:

public class MyClass {
    public static int Add(int a, int b) { return a + b; }
    public static int Add(Tuple<int, int> a) { return a.Item1; }
}

Now I tried to use it on F#:

MyClass.Add(4, 5) |> printf "%d" // prints ... (keep reading!)

So, adding up the three quotations above, one can conclude that:

  • F# will create a Tuple when it sees (4, 5)
  • Then it will call the overload Add(Tuple<int, int>)
  • So it will print 4

To my surprise, it printed 9. Isn't it interesting?

What is really happening here? The above quotations and this practical observations seems to be in contradiction. Can you justify F#'s "reasoning", and maybe pointing to some MSDN docs if possible?

Thanks!

EDIT

(to add more information (from Blindy's answer))

If you do:

MyClass.Add((4, 5)) |> printfn "%d" // prints 9

F# calls the Add(Tuple<int, int>) overload.

However, if you create another F# project (so a different assembly) with this:

namespace MyFSharpNamespace
type MyFShapClass = class
    static member Add x y = x + y
    end

You can use it on C# like this

public static void Main(string[] args) {
    MyFSharpNamespace.MyFSharpClass.Add(4, 5);
}

So far so good. Now, when you try to use it from F# (from another project, another assembly), you have to do:

MyFSharpNamespace.MyFSharpClass.Add 4 5 |> printfn "%d"

If you pass the arguments as (4, 5) F# will not compile because Add is int -> int -> int, and not (int * int) -> int.

What is happening?!?

解决方案

When passing arguments to functions from other .NET libraries, you use a syntax like ".MethodName(parm1, parm2)", that is, the parameters are passed as a Tuple.

It's more hideous than that. See the description of method overload resolution strait from the language spec.

What it says, basically, is that argument in a method invocation isn't really a tuple. It's a syntactic tuple, meaning a comma-separated list of something, but the parentheses are part of the method call syntax, and so are the commas. It's why, for example, o.M(a=1, b=2) isn't a method call with a tuple of two booleans, but rather two named arguments.

So, normally, every comma-separated component just maps to a distinct argument. Hence why Add(1, 2) calls Add(int, int) overload, and Add((1, 2)) calls Add(Tuple<int, int>). There is no ambiguity here.

However, a special case that kicks in for your particular case is this:

If there are no named actual arguments, and there is only one candidate method in M, accepting only one non-optional argument, then the decomposition of arg to tuple form is ignored and there is one named actual arg which is arg itself.

So when you removed all overloads except for the tuple one, suddenly the entire thing inside the parentheses is effectively treated as a tuple constructor in a call. But if you'd e.g. have two overloads, Add(int) and Add(Tuple<int,int>), then a call of the form Add(1,2) wouldn't resolve at all.

这篇关于F# - 传递给C#方法的参数 - 它们是元组还是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆