2012-09-05 30 views
7

Tôi đang tạo và biên dịch biểu thức bằng API System.Ling.Expressions. Biên dịch hoạt động tốt, nhưng trong một số trường hợp tôi nhận được NullReferenceExceptions không giải thích được hoặc thậm chí ngoại lệ System.Security.Verification khi chạy lambda biên dịch. Để tham khảo, mục đích của dự án này là tạo và biên dịch một hàm serializer tùy chỉnh cho một kiểu .NET.Ngoại lệ lạ được biên dịch biểu thức động được xây dựng

Sau đây là DebugInfo cho một biểu thức mà ném một NullReferenceException:

.Lambda #Lambda1<System.Action`2[IO.IWriter,<>f__AnonymousType1`2[System.Int32[],System.Int32]]>(
    IO.IWriter $writer, 
    <>f__AnonymousType1`2[System.Int32[],System.Int32] $t) { 
    .Block() { 
     .Invoke (.Lambda #Lambda2<System.Action`2[IO.IWriter,System.Int32[]]>)(
      $writer, 
      $t.a); 
     .Invoke (.Lambda #Lambda3<System.Action`2[IO.IWriter,System.Int32]>)(
      $writer, 
      $t.b) 
    } 
} 

.Lambda #Lambda2<System.Action`2[IO.IWriter,System.Int32[]]>(
    IO.IWriter $writer, 
    System.Int32[] $t) { 
    .Block() { 
     .Invoke (.Lambda #Lambda4<System.Action`2[IO.IWriter,System.Int32]>)(
      $writer, 
      .Call System.Linq.Enumerable.Count((System.Collections.Generic.IEnumerable`1[System.Int32])$t)); 
     .Call IO.SerializerHelpers.WriteCollectionElements(
      (System.Collections.Generic.IEnumerable`1[System.Int32])$t, 
      $writer, 
      .Lambda #Lambda3<System.Action`2[IO.IWriter,System.Int32]>) 
    } 
} 

.Lambda #Lambda3<System.Action`2[IO.IWriter,System.Int32]>(
    IO.IWriter $writer, 
    System.Int32 $t) { 
    .Call $writer.WriteInt($t) 
} 

.Lambda #Lambda4<System.Action`2[IO.IWriter,System.Int32]>(
    IO.IWriter $w, 
    System.Int32 $count) { 
    .Call $w.BeginWritingCollection($count) 
} 

Trường hợp ngoại lệ được ném trong cuộc gọi đến # Lambda3, được gọi là liên tục từ WriteCollectionElements. Việc thực hiện WriteCollectionElements là như sau:

static void WriteCollectionElements<T>(IEnumerable<T> collection, IWriter writer, Action<IWriter, T> writeAction) 
     { 
      foreach (var element in collection) 
      { 
       writeAction(writer, element); 
      } 
     } 

Từ gỡ lỗi trong chức năng này, tôi đã xác định rằng bộ sưu tập, nhà văn, writeAction, và yếu tố đều không null khi ngoại lệ được ném. Đối số mà tôi chuyển đến lambda đã biên dịch là:

new { a = new[] { 20, 10 }, b = 2 } 

Cũng lạ là nếu tôi xóa thuộc tính b và tạo lại chức năng serializer, mọi thứ đều hoạt động tốt. Trong trường hợp này DebugInfo cho serializer là:

.Lambda #Lambda1<System.Action`2[IO.IWriter,<>f__AnonymousType5`1[System.Int32[]]]>(
    IO.IWriter $writer, 
    <>f__AnonymousType5`1[System.Int32[]] $t) { 
    .Block() { 
     .Invoke (.Lambda #Lambda2<System.Action`2[IO.IWriter,System.Int32[]]>)(
      $writer, 
      $t.a) 
    } 
} 

.Lambda #Lambda2<System.Action`2[IO.IWriter,System.Int32[]]>(
    IO.IWriter $writer, 
    System.Int32[] $t) { 
    .Block() { 
     .Invoke (.Lambda #Lambda3<System.Action`2[IO.IWriter,System.Int32]>)(
      $writer, 
      .Call System.Linq.Enumerable.Count((System.Collections.Generic.IEnumerable`1[System.Int32])$t)); 
     .Call IO.SerializerHelpers.WriteCollectionElements(
      (System.Collections.Generic.IEnumerable`1[System.Int32])$t, 
      $writer, 
      .Lambda #Lambda4<System.Action`2[IO.IWriter,System.Int32]>) 
    } 
} 

.Lambda #Lambda3<System.Action`2[IO.IWriter,System.Int32]>(
    IO.IWriter $w, 
    System.Int32 $count) { 
    .Call $w.BeginWritingCollection($count) 
} 

.Lambda #Lambda4<System.Action`2[IO.IWriter,System.Int32]>(
    IO.IWriter $writer, 
    System.Int32 $t) { 
    .Call $writer.WriteInt($t) 
} 

Tôi đang chạy .NET Framework 4 (ít nhất đó là xây dựng mục tiêu của tôi) trên Windows 7, VS Express C# 2010.

Có ai có bất kỳ ý tưởng những gì có thể đi sai hoặc các bước tiếp theo để cố gỡ lỗi? Tôi rất vui khi đăng thêm thông tin nếu nó sẽ giúp ích.

CHỈNH SỬA: Từ đó đến nay, kiến ​​thức của tôi đã được tìm thấy theo cách của tôi xung quanh lỗi này, mặc dù tôi không hiểu rõ hơn tại sao nó xảy ra. Trong đoạn mã mà tạo ra các biểu thức tôi đã đăng ở trên, tôi đã có những điều sau đây:

MethodInfo writeCollectionElementsMethod = // the methodInfo for WriteCollectionElements with .MakeGenericMethod() called with typeof(T) 
Expression<Action<IWriter, T> writeActionExpression = // I created this expression separately 
ParameterExpression writerParameter, enumerableTParameter = // parameters of type IWriter and IEnumerable<T>, respectively 

// make an expression to invoke the method 
var methodCallExpression = Expression.Call(
    instance: null, // static 
    method: writeCollectionElementsMethod, 
    arguments: new[] { 
     enumerableTParameter, 
     writerParameter, 
     // passing in this expression correctly would produce the weird error in some cases as described above 
     writeActionExpression 
    } 
); 

// make an expression to invoke the method 
var methodCallExpressionV2 = Expression.Call(
    instance: null, // static 
    method: writeCollectionElementsMethod, 
    arguments: new[] { 
     enumerableTParameter, 
     writerParameter, 
     // this did not cause the bug 
     Expression.Constant(writeActionExpression.Compile()) 
    } 
); 

Tuy nhiên, tôi không thích biên soạn mọi biểu hiện riêng rẽ, vì vậy tôi đã kết thúc với việc từ bỏ WriteCollectionElements chức năng hoàn toàn và chỉ tạo vòng lặp foreach động thông qua Expression.Loop, Expression.Break, v.v.

Vì vậy, tôi không còn bị chặn nữa, nhưng vẫn rất tò mò.

+0

Là reproducable ngoại lệ? Nó luôn luôn xảy ra cho cùng một 'nguyên tố'? –

+0

@DanielHilgarth Có, ngoại lệ xảy ra mỗi lần. Nó luôn xảy ra khi xử lý cùng một phần tử, trong trường hợp này là 20. – ChaseMedallion

+1

Có thể bạn có thể tạo một ứng dụng mẫu nhỏ với mã tối thiểu sao chép hành vi này? –

Trả lời

1

Nếu bạn xây dựng Hoạt động bằng tay trong C# resharper phàn nàn về Lambda1 và Lambda2 biến ngầm chụp trong clousure

Action<IWriter, int> lambda4 = ((IWriter writer, int length) => writer.BeginWritingCollection(length)); 
Action<IWriter, int> lambda3 = ((IWriter writer, int value) => writer.WriteInt(value)); 
Action<IWriter, int[]> lambda2 = ((IWriter writer, int[] value) => 
    { 
     lambda4(writer, ((IEnumerable<int>) value).Count()); 
     WriteCollectionElements((IEnumerable<int>)value, writer, lambda3); 
    }); 
Action<IWriter, TheData> lambda1 = ((writer, data) => 
    { 
     lambda2(writer, data.a); 
     lambda3(writer, data.b); 
    }); 
class TheData { int[] a; int b; } 

Trong trạng thái trường hợp resharper:
"ngầm bị bắt đóng cửa: lambda2" trên biểu lambda2
"Đóng cửa hoàn toàn bị bắt: lambda4" trên biểu thức lambda1

Giải thích cho điều này là herehere. Nếu dòng đến WriteCollectionElements bị xóa, cảnh báo sẽ biến mất. Về cơ bản, biên dịch JIT tạo ra một lớp bao bọc cho các lời gọi biểu thức bên trong, nắm bắt các giá trị của người viết và kiểu ẩn danh với mục đích bàn giao hành động cho BeginWritingCollection cho phương thức tĩnh WriteCollectionElements.

Các giải pháp sẽ được nội tuyến báo cáo từ lambda2 vào lambda1

Action<IWriter, int> lambda4 = ((IWriter writer, int length) => writer.BeginWritingCollection(length)); 
Action<IWriter, int> lambda3 = ((IWriter writer, int value) => writer.WriteInt(value)); 
Action<IWriter, TheData> lambda1 = ((writer, data) => 
    { 
     lambda4(writer, ((IEnumerable<int>) value.a).Count()); 
     WriteCollectionElements((IEnumerable<int>)value.a, writer, lambda3); 
     lambda3(writer, data.b); 
    }); 
class TheData { int[] a; int b; } 
+0

biên dịch cả hai tập hợp các hành động và xem DLL trong [phản xạ | dotPeek | justDecompile | ILSpy] và bạn sẽ thấy loại trình bao bọc được tạo. –

+0

Tôi đọc các liên kết và hiểu vấn đề này, nhưng tôi không thấy nó liên quan đến việc sử dụng các biểu thức của tôi như thế nào (như trái ngược với hành động thô/Func) ... – ChaseMedallion

+0

Biểu thức của bạn được chuyển thành hành động tương đương khi bạn biên dịch biểu thức. Bạn không thể "thực hiện" một biểu thức –

Các vấn đề liên quan