簡體   English   中英

F#與C#類互操作,它具有可選的可為空參數設置為null但是null導致NullReferenceException / AccessViolationException

[英]F# interop with C# class that has an optional nullable parameter set to anything but null causes NullReferenceException / AccessViolationException

我有以下C#類

public class BadClass
{
    public BadClass(int? bad = 1)
    {
    }
}

public class GoodClass
{
    public GoodClass(int? good = null)
    {
    }
}

正如您所看到的,它們都具有可選的可空參數作為其構造函數的一部分,唯一的區別是BadClass將參數default設置為null以外的其他值。

如果我嘗試在F#中創建這些類的實例,這就是我得到的:

這很好用:

let g = GoodClass()

這會拋出NullReferenceException:

let b = BadClass()

這會引發AccessViolationException

let asyncB = async { return BadClass() } |> Async.RunSynchronously

知道為什么會這樣嗎?

編輯

使用ILSpy對其進行反編譯,這是F#的輸出

C#類位於一個名為InteopTest的程序集中[原文如此]

ILSpy到C#

GoodClass g = new GoodClass(null);
    BadClass b = new BadClass(1);
    FSharpAsyncBuilder defaultAsyncBuilder = ExtraTopLevelOperators.DefaultAsyncBuilder;
    FSharpAsync<BadClass> fSharpAsync = defaultAsyncBuilder.Delay<BadClass>(new Program.asyncB@10(defaultAsyncBuilder));
    FSharpAsync<BadClass> computation = fSharpAsync;
    BadClass asyncB = FSharpAsync.RunSynchronously<BadClass>(computation, null, null);
    FSharpFunc<string[], Unit> fSharpFunc = ExtraTopLevelOperators.PrintFormatLine<FSharpFunc<string[], Unit>>(new PrintfFormat<FSharpFunc<string[], Unit>, TextWriter, Unit, Unit, string[]>("%A"));
    fSharpFunc.Invoke(argv);
    return 0;

這就是IL

.method public static 
    int32 main (
        string[] argv
    ) cil managed 
{
    .custom instance void [FSharp.Core]Microsoft.FSharp.Core.EntryPointAttribute::.ctor() = (
        01 00 00 00
    )
    // Method begins at RVA 0x2050
    // Code size 92 (0x5c)
    .maxstack 5
    .entrypoint
    .locals init (
        [0] class [InteopTest]InteopTest.GoodClass g,
        [1] valuetype [mscorlib]System.Nullable`1<int32>,
        [2] class [InteopTest]InteopTest.BadClass b,
        [3] class [InteopTest]InteopTest.BadClass asyncB,
        [4] class [FSharp.Core]Microsoft.FSharp.Control.FSharpAsync`1<class [InteopTest]InteopTest.BadClass>,
        [5] class [FSharp.Core]Microsoft.FSharp.Control.FSharpAsyncBuilder builder@,
        [6] class [FSharp.Core]Microsoft.FSharp.Control.FSharpAsync`1<class [InteopTest]InteopTest.BadClass>,
        [7] class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string[], class [FSharp.Core]Microsoft.FSharp.Core.Unit>,
        [8] string[]
    )

    IL_0000: nop
    IL_0001: ldloca.s 1
    IL_0003: initobj valuetype [mscorlib]System.Nullable`1<int32>
    IL_0009: ldloc.1
    IL_000a: newobj instance void [InteopTest]InteopTest.GoodClass::.ctor(valuetype [mscorlib]System.Nullable`1<int32>)
    IL_000f: stloc.0
    IL_0010: ldc.i4.1
    IL_0011: newobj instance void [InteopTest]InteopTest.BadClass::.ctor(valuetype [mscorlib]System.Nullable`1<int32>)
    IL_0016: stloc.2
    IL_0017: call class [FSharp.Core]Microsoft.FSharp.Control.FSharpAsyncBuilder [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::get_DefaultAsyncBuilder()
    IL_001c: stloc.s builder@
    IL_001e: ldloc.s builder@
    IL_0020: ldloc.s builder@
    IL_0022: newobj instance void Program/asyncB@10::.ctor(class [FSharp.Core]Microsoft.FSharp.Control.FSharpAsyncBuilder)
    IL_0027: callvirt instance class [FSharp.Core]Microsoft.FSharp.Control.FSharpAsync`1<!!0> [FSharp.Core]Microsoft.FSharp.Control.FSharpAsyncBuilder::Delay<class [InteopTest]InteopTest.BadClass>(class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Control.FSharpAsync`1<!!0>>)
    IL_002c: stloc.s 4
    IL_002e: ldloc.s 4
    IL_0030: stloc.s 6
    IL_0032: ldloc.s 6
    IL_0034: ldnull
    IL_0035: ldnull
    IL_0036: call !!0 [FSharp.Core]Microsoft.FSharp.Control.FSharpAsync::RunSynchronously<class [InteopTest]InteopTest.BadClass>(class [FSharp.Core]Microsoft.FSharp.Control.FSharpAsync`1<!!0>, class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<int32>, class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<valuetype [mscorlib]System.Threading.CancellationToken>)
    IL_003b: stloc.3
    IL_003c: ldstr "%A"
    IL_0041: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5<class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string[], class [FSharp.Core]Microsoft.FSharp.Core.Unit>, class [mscorlib]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit, string[]>::.ctor(string)
    IL_0046: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatLine<class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string[], class [FSharp.Core]Microsoft.FSharp.Core.Unit>>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<!!0, class [mscorlib]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit>)
    IL_004b: stloc.s 7
    IL_004d: ldarg.0
    IL_004e: stloc.s 8
    IL_0050: ldloc.s 7
    IL_0052: ldloc.s 8
    IL_0054: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string[], class [FSharp.Core]Microsoft.FSharp.Core.Unit>::Invoke(!0)
    IL_0059: pop
    IL_005a: ldc.i4.0
    IL_005b: ret
} // end of method Program::main

對我來說,這看起來像是F#編譯器中的一個錯誤。 如果你寫一些額外的C#:

public class OtherClass
{
    private static BadClass _bc = new BadClass();
}

看看IL,你會看到這個:

// push 1 on the stack
IL_0000:  ldc.i4.1
// call Nullable<int32> constructor, leaving object on stack
IL_0001:  newobj     instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
// call BadClass constructor with int?
IL_0006:  newobj     instance void Nullabool.BadClass::.ctor(valuetype [mscorlib]System.Nullable`1<int32>)
// store in _bc
IL_000b:  stsfld     class Nullabool.BadClass Nullabool.OtherClass::_bc

其中明確地將Nullable`1實例化為1。

而b的F#代碼最終會像這樣:

// push a 1 on the stack
IL_0016:  ldc.i4.1
// call BadClass constructor with 1 - this fails IL verification
IL_0017:  newobj     instance void [Nullabool]Nullabool.BadClass::.ctor(valuetype [mscorlib]System.Nullable`1<int32>)

哪個在堆棧上留下一個int而不是int? 當我嘗試運行此代碼時,由於類型不匹配,我收到IL驗證錯誤。

暫無
暫無

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

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