簡體   English   中英

F# - 關於傳遞給C#方法的參數 - 它們是元組還是什么?

[英]F# - On the parameters passed to C# methods - are they tuples or what?

我讀了很多次

從F#或任何其他.NET語言生成的程序集(幾乎)難以區分。

然后我在.NET 4(beta 2)上試驗F#和C#interop。 我用以下類創建了一個新的解決方案和一個C#項目:

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

然后,在F#項目上,在引用C#項目之后,我嘗試了:

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

到現在為止還挺好。 然后又出現了我多次讀過的另一句話(也許在不同的書上):

將參數傳遞給其他.NET庫中的函數時,使用類似“.MethodName(parm1,parm2)”的語法,即參數作為元組傳遞。

將其添加到我曾經在SO上閱讀的內容(但是無法找到它鏈接到的),在OP試圖創建使用類似[ 4, 5, 6 ] (當他的意思是[4; 5; 6] ):

“逗號是'元組創建運算符',因為其他一切都使用了分號。”

然后我將我的課修改為以下內容:

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

現在我嘗試在F#上使用它:

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

因此,加上上面的三個引文,可以得出結論:

  • F#會在看到時創建一個元組(4, 5)
  • 然后它將調用重載Add(Tuple<int, int>)
  • 所以它將打印4

令我驚訝的是, 它打印了9 不是很有趣嗎?

這里到底發生了什么? 上述引文和這些實際觀察似乎是矛盾的。 你可以證明F#的“推理”,並且如果可能的話可能指向一些MSDN文檔嗎?

謝謝!

編輯

(添加更多信息(來自Blindy的回答))

如果你這樣做:

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

F#調用Add(Tuple<int, int>)重載。

但是,如果您使用以下方法創建另一個F#項目(因此使用不同的程序集):

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

您可以像這樣在C#上使用它

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

到現在為止還挺好。 現在,當您嘗試從F#(來自另一個項目,另一個程序集)使用它時,您必須:

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

如果將參數傳遞為(4, 5) F#將無法編譯,因為Addint -> int -> int ,而不是(int * int) -> int

怎么了?!?

將參數傳遞給其他.NET庫中的函數時,使用類似“.MethodName(parm1,parm2)”的語法,即參數作為元組傳遞。

它比那更可怕。 語言規范中查看方法重載分辨率strait的描述。

基本上,它所說的是方法調用中的參數實際上並不是一個元組。 它是一個語法元組,意思是逗號分隔的某些東西,但括號是方法調用語法的一部分,逗號也是如此。 這就是為什么,例如, oM(a=1, b=2)不是一個帶有兩個布爾元組的方法調用,而是兩個命名參數。

因此,通常,每個逗號分隔的組件只映射到一個不同的參數。 因此,為什么Add(1, 2)調用Add(int, int)重載, Add((1, 2))調用Add(Tuple<int, int>) 這里沒有歧義。

但是,針對您的特定情況啟動的特殊情況是:

如果沒有命名的實際參數,並且M只有一個候選方法,只接受一個非可選參數,那么將忽略arg到元組形式的分解,並且有一個名為actual arg ,即arg本身。

因此,當您刪除除元組之外的所有重載時,括號內的所有內容突然被有效地視為調用中的元組構造函數。 但是如果你有兩個重載, Add(int)Add(Tuple<int,int>) ,那么Add(1,2)形式的調用根本就不會解決。

我現在沒有安裝F#,但在我看來

MyClass.Add(4, 5) |> printf "%d"

將打印9而不是

MyClass.Add((4, 5)) |> printf "%d"

會打印.. 4對嗎? 注意雙重parantheses,標記元組的內部對和標記函數調用的外部對。

這只是編譯魔術。

let add a b = a+b 

add編譯以add(a,b) ,使得從C#調用變得容易。 但是,由於IL中的屬性,F#程序仍然將其視為add ab

在F#中調用C#函數時,可能有助於將C#函數視為只有一個參數 - 元組,其元素確定正確的重載。 所以你可以寫:

// MyClass.Add(5,3) = 8
let eight = (5,3) |> MyClass.Add

我不是F#專家,所以我可能有點偏離基礎,但我猜測元組的F#概念與BCL System.Tuple類型無關。 元組是F#的核心原則,並且內置於語言中,但C#,VB.NET和大多數其他.NET語言本身不支持元組。 由於元組在這些語言中很有用,因此庫正在獲得對它們的支持。

我想進一步假設F#元組在內存中表示的方式與將參數傳遞給C#和朋友中的方法的方式非常相似。 也就是說,它們本質上是其組件的值數組。 當將此值數組推送到堆棧以進行方法調用時,它將具有與將其每個組成組件推送到堆棧上相同的效果,就像從C#調用該方法一樣。

因此,您的第二個示例創建一個F#元組,將其推送到堆棧,然后調用Add重載,該重載采用元組中包含的類型。

無論如何,這是我的猜測。 大概使用F#比我更多,你可能會有更多的洞察力。 您還可以通過查看生成的代碼Reflector獲得更多線索。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM