2012-09-05 14 views
7

मैं सिस्टम के साथ एक अभिव्यक्ति बना रहा हूं और संकलित कर रहा हूं। Ling.Expressions API। संकलन ठीक काम करता है, लेकिन कुछ मामलों में मुझे संकलित lambda चलाते समय अस्पष्ट NullReferenceExceptions या यहां तक ​​कि System.Security.Verification अपवाद मिलता है। संदर्भ के लिए, इस प्रोजेक्ट का उद्देश्य एक .NET प्रकार के लिए कस्टम सीरिएलाइज़र फ़ंक्शन बनाना और संकलित करना है।अजीब अपवाद संकलित गतिशील रूप से निर्मित अभिव्यक्ति संकलित

निम्नलिखित एक अभिव्यक्ति है कि एक NullReferenceException फेंकता के लिए DebugInfo है:

.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) 
} 

अपवाद # करने के लिए कॉल Lambda3, जो WriteCollectionElements से बार-बार कहा जाता है के भीतर फेंक दिया है।

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

इस समारोह के अंदर डिबगिंग से, मैं निर्धारित किया है कि संग्रह, लेखक, writeAction, और तत्व सभी में गैर-शून्य कर रहे हैं जब अपवाद फेंक दिया जाता है: WriteCollectionElements के कार्यान्वयन इस प्रकार है। तर्क यह है कि मैं संकलित लैम्ब्डा के लिए गुजर रहा है:

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

भी अजीब है कि अगर मैं ख संपत्ति को हटाने और मेरी serializer समारोह दोबारा बनाने के, सब कुछ ठीक काम करता है। इस मामले में serializer के लिए DebugInfo है:

.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) 
} 

मैं विंडोज 7 पर .NET फ्रेमवर्क 4 (कम से कम है कि अपने निर्माण लक्ष्य है) चला रहा हूँ, वी.एस. एक्सप्रेस सी # 2010

किसी को भी किसी भी विचार है डीबग करने का प्रयास करने के लिए क्या गलत हो सकता है या अगले चरण क्या हो सकते हैं? अगर इससे मदद मिलेगी तो मुझे अधिक जानकारी पोस्ट करने में खुशी होगी।

संपादित करें: मैंने तब से (मेरे ज्ञान के लिए) इस बग के आसपास अपना रास्ता खोज लिया है, हालांकि मुझे यह समझने के करीब नहीं है कि ऐसा क्यों होता है।

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()) 
    } 
); 

हालांकि, मैं अलग से हर अभिव्यक्ति संकलन पसंद नहीं आया, तो मैं WriteCollectionElements समारोह के साथ भाग पूरी तरह और बस कर समाप्त हो गया: कोड है कि भाव मैंने ऊपर पोस्ट किया है उत्पन्न में, मैं निम्नलिखित था अभिव्यक्ति के माध्यम से गतिशील लूप को गतिशील रूप से बनाना। लूप, अभिव्यक्ति। ब्रेक, आदि

इस प्रकार, अब मैं अवरुद्ध नहीं हूं, लेकिन अभी भी बहुत उत्सुक हूं।

+0

अपवाद reproducable है हो सकता है? क्या यह हमेशा एक ही तत्व के लिए होता है? –

+0

@DanielHilgarth हां, अपवाद हर बार होता है। यह हमेशा होता है जब एक ही तत्व को संसाधित करते हैं, इस मामले में 20. – ChaseMedallion

+1

हो सकता है कि आप इस व्यवहार को पुन: उत्पन्न करने वाले न्यूनतम कोड के साथ एक छोटा सा नमूना अनुप्रयोग बना सकें? –

उत्तर

1

आप क्रिया मैन्युअल में सी # ReSharper 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; } 

में Lambda1 और Lambda2 परोक्ष कब्जा चर इस मामले ReSharper राज्यों में के बारे में शिकायत का निर्माण करते हैं:
"उलझाव से कब्जा कर लिया बंद: lambda2" lambda2 अभिव्यक्ति पर lambda1 अभिव्यक्ति

इस के लिए विवरण here और here है पर:
"lambda4 उलझाव से कब्जा कर लिया बंद"। यदि WriteCollectionElements को जोड़ने की रेखा हटा दी जाती है, तो चेतावनी गायब हो जाती है। अनिवार्य रूप से जेआईटी संकलन आंतरिक अभिव्यक्ति कॉल के लिए एक रैपर वर्ग बनाता है, लेखक के मूल्यों और अज्ञात प्रकार को कैप्चर करने के उद्देश्य से WriteCollectionElements static विधि में BeginWritingCollection के लिए कार्रवाई को सौंपने के उद्देश्य से बनाता है।

समाधान lambda1 में lambda2 से बयान इनलाइन को

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

कार्यों के दोनों सेट संकलित करें और [परावर्तक में डीएलएल देखें। डॉटपीक | बस डिकंपाइल | ILSpy] और आप बनाया रैपर प्रकार देखेंगे। –

+0

मैंने लिंक पढ़े और इस मुद्दे को समझ लिया, लेकिन मैं यह देखने में असफल रहा कि यह अभिव्यक्तियों के उपयोग से संबंधित है (कच्चे क्रियाओं/Funcs के विपरीत) ... – ChaseMedallion

+0

अभिव्यक्ति संकलित करते समय आपके अभिव्यक्ति समकक्ष कार्रवाई में परिवर्तित हो जाती हैं। आप –

संबंधित मुद्दे