2012-02-15 17 views
14

Một đồng nghiệp đã chuyển cho tôi một mẫu mã thú vị treo với InvalidProgramException ("CLR đã phát hiện một Chương trình Không hợp lệ") khi chạy."CLR đã phát hiện một Chương trình Không hợp lệ" khi sử dụng Enumerable.ToDictionary với phương thức mở rộng

Vấn đề dường như xảy ra vào thời điểm JIT, trong đó biên dịch tốt nhưng ném ngoại lệ ngay trước khi phương thức có dòng "vi phạm" được gọi - tôi đoán là nó đang được JIT.

Dòng đang đề cập đang gọi Enumerable.ToDictionary và chuyển qua đối số thứ hai là Func.

Nếu đối số Func được chỉ định đầy đủ với lambda nó hoạt động; nếu nó được chỉ định như là một nhóm phương pháp, nếu không thành công. Chắc chắn hai là tương đương?

Điều này khiến tôi bối rối (và đồng nghiệp đã phát hiện ra nó!) - và chắc chắn nó có vẻ giống như lỗi JIT.

[EDIT: Xin lỗi - Tôi đã nhận đèo và không trường hợp một cách sai lầm vòng trong mẫu mã - bây giờ điều chỉnh (mô tả ở trên là đúng)]

Có ai có một lời giải thích?

using System; 
using System.Linq; 

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     Test.Try(); 
    } 
} 

public class Test 
{ 
    public static readonly int[] integers = new[] { 1, 3, 5 }; 
    public static void Try() 
    { 
     var line = new Line { A = 3, B = 5 }; 

     // PASSES 
     var dict = integers.ToDictionary<int, int, decimal>(i => i, i => line.Compute(i)); 

     // FAILS 
     //var dict = integers.ToDictionary<int, int, decimal>(i => i, line.Compute); 

     Console.WriteLine(string.Join(" ", dict.Select(kv => kv.Key + "-" + kv.Value))); 
    } 
} 

public class Line 
{ 
    public decimal A; 
    public decimal B; 
} 

public static class SimpleCompute 
{ 
    public static decimal Compute(this Line line, int value) 
    { 
     return line.A*value + line.B; 
    } 
} 
+0

Trên thực tế, trên bản cài đặt cục bộ, dòng "PASSES" của bạn không thành công quá –

+0

Nó hoạt động tốt trên hộp (v4.0, x86) của tôi - bạn đang chạy phiên bản .NET nào và kiến ​​trúc nào? –

+0

Thú vị! Nếu bạn loại bỏ các phương pháp mở rộng (tức là đặt SimpleCompute như là một phương pháp dụ trên Line) nó hoạt động? Nó làm cho tôi nếu tôi làm điều này? –

Trả lời

15

Lỗi trình biên dịch.

Để biết thông tin, tôi có CTP không đồng bộ, có thể có liên quan; csc báo cáo: 4.0.30319.440

Có vẻ là một sự khác biệt giữa:

public static void TryTwo() // fails 
{ 
    var line = new Line {A = 3, B = 5}; 

    var dict = integers.ToDictionary<int, int, decimal>(i => i, line.Compute); 
    Console.WriteLine("TryTwo complete"); 
} 
public static void TryFive() // works 
{ 
    var line = new Line { A = 3, B = 5 }; 

    Func<int, decimal> func = line.Compute; 
    var dict = integers.ToDictionary<int, int, decimal>(i => i, func); 
    Console.WriteLine("TryFour complete"); 
} 

vì vậy hãy nhìn vào gương:

.method public hidebysig static void TryTwo() cil managed 
{ 
    .maxstack 4 
    .locals init (
     [0] class Line <>g__initLocal6) 
    L_0000: newobj instance void Line::.ctor() 
    L_0005: stloc.0 
    L_0006: ldloc.0 
    L_0007: ldc.i4.3 
    L_0008: newobj instance void [mscorlib]System.Decimal::.ctor(int32) 
    L_000d: stfld valuetype [mscorlib]System.Decimal Line::A 
    L_0012: ldloc.0 
    L_0013: ldc.i4.5 
    L_0014: newobj instance void [mscorlib]System.Decimal::.ctor(int32) 
    L_0019: stfld valuetype [mscorlib]System.Decimal Line::B 
    L_001e: ldsfld int32[] Test::integers 
    L_0023: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
    L_0028: brtrue.s L_003b 
    L_002a: ldnull 
    L_002b: ldftn int32 Test::<TryTwo>b__7(int32) 
    L_0031: newobj instance void [mscorlib]System.Func`2<int32, int32>::.ctor(object, native int) 
    L_0036: stsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
    L_003b: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
    L_0040: call class [mscorlib]System.Collections.Generic.Dictionary`2<!!1, !!2> [System.Core]System.Linq.Enumerable::ToDictionary<int32, int32, valuetype [mscorlib]System.Decimal>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, !!1>, class [mscorlib]System.Func`2<!!0, !!2>) 
    L_0045: pop 
    L_0046: ldstr "TryTwo complete" 
    L_004b: call void [mscorlib]System.Console::WriteLine(string) 
    L_0050: ret 
} 

vs

.method public hidebysig static void TryFive() cil managed 
{ 
    .maxstack 4 
    .locals init (
     [0] class Line line, 
     [1] class [mscorlib]System.Func`2<int32, valuetype [mscorlib]System.Decimal> func, 
     [2] class Line <>g__initLocal9) 
    L_0000: newobj instance void Line::.ctor() 
    L_0005: stloc.2 
    L_0006: ldloc.2 
    L_0007: ldc.i4.3 
    L_0008: newobj instance void [mscorlib]System.Decimal::.ctor(int32) 
    L_000d: stfld valuetype [mscorlib]System.Decimal Line::A 
    L_0012: ldloc.2 
    L_0013: ldc.i4.5 
    L_0014: newobj instance void [mscorlib]System.Decimal::.ctor(int32) 
    L_0019: stfld valuetype [mscorlib]System.Decimal Line::B 
    L_001e: ldloc.2 
    L_001f: stloc.0 
    L_0020: ldloc.0 
    L_0021: ldftn valuetype [mscorlib]System.Decimal SimpleCompute::Compute(class Line, int32) 
    L_0027: newobj instance void [mscorlib]System.Func`2<int32, valuetype [mscorlib]System.Decimal>::.ctor(object, native int) 
    L_002c: stloc.1 
    L_002d: ldsfld int32[] Test::integers 
    L_0032: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegateb 
    L_0037: brtrue.s L_004a 
    L_0039: ldnull 
    L_003a: ldftn int32 Test::<TryFive>b__a(int32) 
    L_0040: newobj instance void [mscorlib]System.Func`2<int32, int32>::.ctor(object, native int) 
    L_0045: stsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegateb 
    L_004a: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegateb 
    L_004f: ldloc.1 
    L_0050: call class [mscorlib]System.Collections.Generic.Dictionary`2<!!1, !!2> [System.Core]System.Linq.Enumerable::ToDictionary<int32, int32, valuetype [mscorlib]System.Decimal>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, !!1>, class [mscorlib]System.Func`2<!!0, !!2>) 
    L_0055: pop 
    L_0056: ldstr "TryFour complete" 
    L_005b: call void [mscorlib]System.Console::WriteLine(string) 
    L_0060: ret 
} 

Nếu bạn nhìn vào phiên bản bị hỏng, nó chỉ tải một đại biểu. Biên dịch lỗi, về cơ bản:

L_0023: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
L_0028: brtrue.s L_003b 
L_002a: ldnull 
L_002b: ldftn int32 Test::<TryTwo>b__7(int32) 
L_0031: newobj instance void [mscorlib]System.Func`2<int32, int32>::.ctor(object, native int) 
L_0036: stsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
L_003b: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
L_0040: call class [mscorlib]System.Collections.Generic.Dictionary`2<!!1, !!2> [System.Core]System.Linq.Enumerable::ToDictionary<int32, int32, valuetype [mscorlib]System.Decimal>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, !!1>, class [mscorlib]System.Func`2<!!0, !!2>) 

tất cả những điều trên là "kiểm tra xem cache i => i tồn tại, nếu không tạo ra nó, sau đó tải nó". Nó không bao giờ làm bất cứ điều gì với đại biểu thứ hai. Do đó, không có đủ giá trị trên ngăn xếp để thực hiện cuộc gọi phương thức.

+0

Cảm ơn Marc. Mô tả của bạn giải thích lỗi StackUnderflow mà PEVerify báo cáo về lắp ráp quá (tôi có lẽ nên đã đề cập rằng trong câu hỏi ban đầu). Nhiều đánh giá cao! –

+0

@Rob chỉ để thêm: điều này đã được chuyển đến các kênh thích hợp để sửa chữa. –

+0

Cảm ơn một lần nữa Marc - Tôi đã tự hỏi phải làm gì về điều đó, nhưng bạn đã sắp xếp nó. Một lần nữa, rất nhiều đánh giá cao. –