Tworzę i kompilowanie wyrażenie z API System.Ling.Expressions. Kompilacja działa dobrze, ale w niektórych przypadkach pojawiają się niewyjaśnione wyjątki NullReferenceException, a nawet wyjątki System.Security.Verification podczas uruchamiania skompilowanej analizy lambda. Dla odniesienia, celem tego projektu jest utworzenie i skompilowanie niestandardowej funkcji serializera dla typu .NET.Weird wyjątki skompilowane dynamicznie zbudowany wyrażenie
Poniżej przedstawiono DebugInfo na wyrażeniu generuje 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)
}
jest wyjątek w wywołaniu # Lambda3, które nazywa się kilkakrotnie z WriteCollectionElements. Realizacja WriteCollectionElements jest następujący:
static void WriteCollectionElements<T>(IEnumerable<T> collection, IWriter writer, Action<IWriter, T> writeAction)
{
foreach (var element in collection)
{
writeAction(writer, element);
}
}
Od debugowania wewnątrz tej funkcji I ustalili, że kolekcja, pisarz, writeAction i elementem są niezerowe, gdy jest wyjątek. Argument, że ja przechodząc do skompilowany lambda jest:
new { a = new[] { 20, 10 }, b = 2 }
też dziwnego, że jeśli usunąć właściwości b i ponownie wygenerować mojej funkcji serializer, wszystko działa bez zarzutu. W tym przypadku DebugInfo dla serializatora jest:
.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)
}
Używam .NET Framework 4 (przynajmniej to jest mój cel build) w systemie Windows 7, VS ekspresowe C# 2010.
Czy ktoś ma jakiś pomysł co może być nie tak lub jakie są dalsze kroki, aby spróbować debugować? Z przyjemnością opublikuję więcej informacji, jeśli to pomoże.
EDYCJA: Odkąd (według mojej wiedzy) znalazłem drogę wokół tego błędu, chociaż nie jestem bliżej zrozumienia, dlaczego tak się dzieje. W kodzie, który generuje wyrażeń Mam zamieszczonych powyżej, miałem następujące:
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())
}
);
jednak mi się nie podoba kompilacji każdy wyraz osobno, więc skończyło się na rezygnacji z funkcji WriteCollectionElements całkowicie i po prostu dynamiczne tworzenie pętli foreach za pomocą Expression.Loop, Expression.Break, itp.
W ten sposób nie jestem już zablokowany, ale nadal jestem bardzo ciekawy.
Czy odtwarzalny wyjątek? Czy to zawsze dzieje się dla tego samego "elementu"? –
@ Daniel Hilgarth Tak, wyjątek ma miejsce za każdym razem. Zawsze dzieje się to przy przetwarzaniu tego samego elementu, w tym przypadku 20. – ChaseMedallion
Może można utworzyć małą przykładową aplikację z minimalnym kodem, który odtwarza to zachowanie? –