2013-04-23 3 views
6

Dostaję System.InvalidOperationException: Collection was modified; enumeration operation may not execute:Otrzymuję `System.InvalidOperationException: Collection został zmodyfikowany; Operacja wyliczenie może nie execute`, niewytłumaczalny

ExceptionLoggingLibrary.LoggingException: Exception of type 'ExceptionLoggingLibrary.LoggingException' was thrown. ---> System.InvalidOperationException: Collection was modified; enumeration operation may not execute. 
at System.Collections.Generic.List`1.Enumerator.MoveNextRare() 
at iTextSharp.text.FontFactoryImp.GetFont(String fontname, String encoding, Boolean embedded, Single size, Int32 style, BaseColor color, Boolean cached) 
[...] 

Z tego co rozumiem, że wyjątek dzieje, gdy IEnumerable obiekt został zmodyfikowany podczas jego wyliczenia.

Oto metoda iTextSharp.text.FontFactoryImp.GetFont:

public virtual Font GetFont(string fontname, string encoding, bool embedded, float size, int style, BaseColor color, bool cached) { 
     if (fontname == null) return new Font(Font.FontFamily.UNDEFINED, size, style, color); 
     string lowercasefontname = fontname.ToLower(CultureInfo.InvariantCulture); 
     List<string> tmp; 
     fontFamilies.TryGetValue(lowercasefontname, out tmp); 
     if (tmp != null) { 
      // some bugs were fixed here by Daniel Marczisovszky 
      int fs = Font.NORMAL; 
      bool found = false; 
      int s = style == Font.UNDEFINED ? Font.NORMAL : style; 
      foreach (string f in tmp) { 
       string lcf = f.ToLower(CultureInfo.InvariantCulture); 
       fs = Font.NORMAL; 
       if (lcf.ToLower(CultureInfo.InvariantCulture).IndexOf("bold") != -1) fs |= Font.BOLD; 
       if (lcf.ToLower(CultureInfo.InvariantCulture).IndexOf("italic") != -1 || lcf.ToLower(CultureInfo.InvariantCulture).IndexOf("oblique") != -1) fs |= Font.ITALIC; 
       if ((s & Font.BOLDITALIC) == fs) { 
        fontname = f; 
        found = true; 
        break; 
       } 
      } 
      if (style != Font.UNDEFINED && found) { 
       style &= ~fs; 
      } 
     } 
     BaseFont basefont = null; 
     try { 
      try { 
       // the font is a type 1 font or CJK font 
       basefont = BaseFont.CreateFont(fontname, encoding, embedded, cached, null, null, true); 
      } 
      catch (DocumentException) { 
      } 
      if (basefont == null) { 
       // the font is a true type font or an unknown font 
       trueTypeFonts.TryGetValue(fontname.ToLower(CultureInfo.InvariantCulture), out fontname); 
       // the font is not registered as truetype font 
       if (fontname == null) return new Font(Font.FontFamily.UNDEFINED, size, style, color); 
       // the font is registered as truetype font 
       basefont = BaseFont.CreateFont(fontname, encoding, embedded, cached, null, null); 
      } 
     } 
     catch (DocumentException de) { 
      // this shouldn't happen 
      throw de; 
     } 
     catch (System.IO.IOException) { 
      // the font is registered as a true type font, but the path was wrong 
      return new Font(Font.FontFamily.UNDEFINED, size, style, color); 
     } 
     catch { 
      // null was entered as fontname and/or encoding 
      return new Font(Font.FontFamily.UNDEFINED, size, style, color); 
     } 
     return new Font(basefont, size, style, color); 
    } 

Gdzie w tej metodzie jest to możliwe, że IEnumerable obiekt jest modyfikowane podczas wyliczania?

+8

Wygląda na to, że inny wątek modyfikuje kolekcję podczas działania tego kodu, ponieważ ta metoda w ogóle nie modyfikuje kolekcji. Twój problem jest gdzieś indziej w twoim kodzie. – Servy

+1

Jeśli modyfikowana kolekcja to tmp, dlaczego nie wykonać głębokiej kopii listy po jej zwróceniu, aby nie trzeba było się martwić, że zostanie zmodyfikowana w innym miejscu? – Chris

+0

@CLandry, nie wiem, to nie jest moja biblioteka. Po prostu próbuję dojść do sedna. – mattalxndr

Odpowiedz

8

Nie wiedząc, co jest w środku swoimi metodami, to uniemożliwić Odbiór zmieniane podczas wyliczania:

Zmiana:

List<string> tmp; 
fontFamilies.TryGetValue(lowercasefontname, out tmp); 

Do:

List<string> sharedList; 
fontFamilies.TryGetValue(lowercasefontname, out sharedList); 
var tmp = new List<string>(sharedList); 

To daje nowa lista, z której możesz być pewien, że nigdzie indziej nie uzyskuje się dostępu do żadnych innych wątków, ponieważ gwarantowane jest nie tylko odwoływanie się do listy w TryGetValue().

Zmieniłem nazwę listy tmp z poprzedniej i nazwał nową listę tmp, aby nie trzeba było zmieniać żadnego innego kodu.