2014-01-07 12 views
7

Ich habe folgende C# -KlassenF # Interop mit C# Klasse, die eine optionale Nullable-Parameter auf alles andere als null hat bewirkt, dass Nullreferenceexception/Access

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

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

Wie Sie sehen, haben sie beide optional Nullable-Parameter als Teil ihrer Konstruktoren, der einzige Unterschied ist, dass in BadClass der Parameter default auf einen anderen Wert als null gesetzt ist.

Wenn ich versuchen, eine Instanz dieser Klassen in F # dieses ist zu schaffen, was ich bekommen:

Dies funktioniert:

let g = GoodClass() 

Dieses eine Nullreferenceexception wirft:

let b = BadClass() 

Und Dies löst eine AccessViolationException

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

Irgendeine Idee, warum das ist?

EDIT

ILSpy Verwendung decompile Dies ist der Ausgang der F #

C# Klassen in ein InteopTest [sic]

ILSpy bis C#

GoodClass g = new GoodClass(null); 
    BadClass b = new BadClass(1); 
    FSharpAsyncBuilder defaultAsyncBuilder = ExtraTopLevelOperators.DefaultAsyncBuilder; 
    FSharpAsync<BadClass> fSharpAsync = defaultAsyncBuilder.Delay<BadClass>(new [email protected](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; 
genannten Baugruppe sind

und dies ist die 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 [email protected], 
     [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 [email protected] 
    IL_001e: ldloc.s [email protected] 
    IL_0020: ldloc.s [email protected] 
    IL_0022: newobj instance void Program/[email protected]::.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 
+1

Welche Version von F #? Funktioniert gut in v3.0 (VS 2013). – Daniel

+0

Es ist v3.1 in Vs2013 – TWith2Sugars

+1

Leider habe ich im Moment keinen Zugriff auf v3.1. Hoffentlich wird jemand anderes dazu in der Lage sein Haben Sie [decompiling] (http://www.telerik.com/products/decompiler.aspx) versucht? – Daniel

Antwort

6

Für mich sieht das wie ein Fehler im F # -Compiler. Wenn Sie etwas mehr C# schreiben:

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

und Blick auf die IL, werden Sie sehen:

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

die eindeutig Nullable`1 mit 1.

Während die F # -Code instanziiert für b endet, wie dies oben:

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

die eine int auf dem Stapel anstelle vonBlätter. Wenn ich versuche, diesen Code auszuführen, erhalte ich einen IL-Verifizierungsfehler, da der Typ nicht übereinstimmt.

+0

aktualisieren Ich bin nicht so kompetent in IL, aber wo instanziiert es mit 1? – TWith2Sugars

+1

Hinzugefügt Kommentare für Sie. – plinth

+0

Ah ich sehe jetzt, ich werde um den F # Compiler-Quelle graben und sehen, ob ich finden kann, wo das passiert. – TWith2Sugars