【问题标题】:I'm getting `System.InvalidOperationException: Collection was modified; enumeration operation may not execute`, inexplicably我得到`System.InvalidOperationException:集合已修改;枚举操作可能无法执行`,莫名其妙
【发布时间】:2013-04-23 18:28:13
【问题描述】:

我收到System.InvalidOperationException: Collection was modified; enumeration operation may not execute:

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)
[...]

据我了解,当 IEnumerable 对象在其枚举期间被修改时,就会发生该异常。

这是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);
    }

在该方法中,IEnumerable 对象可能在枚举期间被修改?

【问题讨论】:

  • 看起来像另一个线程正在修改集合,而这段代码正在运行,因为这个方法根本没有修改集合。您的问题在代码的其他地方。
  • 如果被修改的集合是tmp,为什么不在列表返回的时候做一个深拷贝,这样你就不用担心它在其他地方被修改了?
  • @CLandry,不确定,这不是我的图书馆。我只是想深入了解它。
  • 引发此异常是因为 other 代码在 another 时间修改了集合。当 GetFont() 得到它时,损坏已经造成。是的,很难调试,它需要一台带水晶球的时间机器。

标签: c# .net ienumerable


【解决方案1】:

在不知道你的方法里面有什么的情况下,这将防止你的 Collection 在枚举期间被更改:

变化:

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

收件人:

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

这将为您提供一个新列表,您可以确定它不会被任何其他线程在其他任何地方访问,因为它保证不仅仅是对 TryGetValue() 中列表的引用。

我已经更改了 tmp 列表的名称,并将新列表命名为 tmp,这样您就不需要更改任何其他代码。

【讨论】:

    猜你喜欢
    • 2020-02-07
    • 2022-06-17
    • 2011-10-05
    • 1970-01-01
    • 2015-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多