C# 问题诊断 InvalidOperationException

C# Issue diagnosting InvalidOperationException

我正在编写一个小应用程序来修改音频文件的 ID3 标签。 UI 非常简单:您选择媒体库,然后可以递归地将各种代码逻辑应用于所有文件。

我正在使用 TagLib 在我的项目中读写标签。

为了使用图书馆,我需要阅读所有标签并将它们存储在相册列表中。

问题:在函数 private void GenerateClassifiedLibrary(List files) 中抛出 InvalidOperationException,我找不到任何原因,为什么会这样。

这是主应用程序的源代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;

namespace Music_Metadata_Experiments
{
    public partial class Form1 : Form
    {
        // Global Variables Declaration
        public static List<Album> classifiedLibrary = new List<Album>();
        // classifiedLibrary is a List of Albums
        // Albums contain Songs
        // Songs contain Tags like "TITLE", "ARTIST", "ALBUM", "GENRE", etc. which can be read and displayed by every Media Player.

        // Start up
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            txtLibraryPath.Text = Environment.GetEnvironmentVariable("USERPROFILE") + "\Music";
        }

        private void BtnSelectLibrary_Click(object sender, EventArgs e)
        {
            String libraryPath = "";
            List<String> musicFiles;

            if (libraryBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                libraryPath = libraryBrowserDialog.SelectedPath;
            }

            if (libraryPath != null && libraryPath != "")
            {
                txtLibraryPath.Text = libraryPath;
                Log("New Library: " + libraryPath);
            }

            MessageBox.Show("Mediathek wird eingelesen. Dieser Vorgang kann je nach Größe sehr lange dauern.\nDrücke Strg+Shift+Esc für mehr technische Infos.\n\nBitte warten!");

            // Reading the Library:
            musicFiles = GetFilesFromLibrary(libraryPath);
            GenerateClassifiedLibrary(musicFiles);
        }

        // Adjustable Code
        private void ExecuteLogic1_Click(object sender, EventArgs e)
        { // Not implemented yet
            // Goal: Fix this problem -> Song 1 from DISKNUMBER 1 and Song 1 from DISKNUMBER 2 are shown underneath each other, because DISKNUMBER is missing or corrupted.
            // Function: Finds multiple Songs having TITLENUMBER == 1 but are in the same ALBUM and have the same DISKNUMBER or no DISKNUMBER
            Log("Executing Logic 1");

            Log("Library Count: " + classifiedLibrary.Count);
            foreach (Album album in classifiedLibrary)
            {
                Log("Album: " + album.Name);
            }
        }

        private void ExecuteLogic2_Click(object sender, EventArgs e)
        {

        }

        private void ExecuteLogic3_Click(object sender, EventArgs e)
        {

        }

        private void ExecuteLogic4_Click(object sender, EventArgs e)
        {

        }

        private void ExecuteLogic5_Click(object sender, EventArgs e)
        {

        }

        private void ExecuteLogic6_Click(object sender, EventArgs e)
        {

        }

        private void ExecuteLogic7_Click(object sender, EventArgs e)
        {

        }

        private void ExecuteLogic8_Click(object sender, EventArgs e)
        {

        }

        // Helper Methods
        private List<String> GetFilesFromLibrary(string libraryPath)
        {
            String currentFileName = null;
            String fileExtensionToScanFor = "mp3";
            bool skipOnError = true;
            List<String> files = new List<String>();

            try
            {
                foreach (string file in Directory.EnumerateFiles(libraryPath, "*." + fileExtensionToScanFor, SearchOption.AllDirectories))
                {
                    currentFileName = file;
                    files.Add(file);
                }
                Log("Found " + files.Count + " Files in Library");
            }
            catch (Exception any)
            {
                Console.WriteLine("Erroar reading file: " + currentFileName + " // Problem: " + any.Message);

                if (skipOnError == false)
                {
                    Application.Exit();
                }
            }
            return files;
        }

        private void GenerateClassifiedLibrary(List<String> files)
        {
            Log("Generating a Library to work with");
            TagLib.File currentFile = null;

            foreach (string song in files) // ALL Songs found in Library Folder
            {
                try
                {
                    currentFile = TagLib.File.Create(song);

                    String trackAlbum = currentFile.Tag.Album;

                    // Add at least 1 Album to classifiedLibrary
                    if (classifiedLibrary.Count < 1)
                    {
                        List<TagLib.File> albumTrackList = new List<TagLib.File>();
                        albumTrackList.Add(currentFile);
                        Album firstAlbum = new Album(trackAlbum, albumTrackList);

                        classifiedLibrary.Add(firstAlbum);
                    }

                    // Find an Album in classifiedLibrary to which this track belongs to
                    foreach (Album album in classifiedLibrary) // FIXME: InvalidOperationException thrown HERE
                    {
                        Console.WriteLine("Album: " + album.Name);
                        Console.WriteLine("Track: " + currentFile.Tag.Title); // not matching to album above

                        if (album.Name == trackAlbum)
                        {
                            // Album found in classifiedLibrary
                            Log("EXISTING Album: " + trackAlbum + " | Track Count = " + album.Tracks.Count);

                            // Now checking if the track exists or add it
                            List<TagLib.File> tracks = album.Tracks;

                            foreach (TagLib.File track in tracks)
                            {
                                if (track == currentFile)
                                {
                                    // Already added to this Album!
                                    Log("Already added: " + track.Tag.Title);
                                }
                                else
                                {
                                    if (track.Tag.Album == currentFile.Tag.Album)
                                    {
                                        tracks.Add(track);
                                        Log("X Added Track '" + track.Tag.Title + "' to '" + album.Name + "'.");
                                    }
                                    else
                                    {
                                        // Next one.
                                    }
                                    // Next one.
                                }
                                // Next one.
                            }
                            album.Tracks = tracks;
                        }
                        else
                        {
                            // Album not found in classifiedLibrary - FIXME: Album existiert und wird trotzdem neu erstellt
                            Log("NEW Album: " + trackAlbum);

                            List<TagLib.File> trackOrigin = new List<TagLib.File>();
                            Album newAlbum = new Album(trackAlbum, trackOrigin);
                            newAlbum.Tracks.Add(currentFile);
                            Log("Y Added Track '" + currentFile.Tag.Title + "' to '" + newAlbum.Name + "'.");

                            classifiedLibrary.Add(newAlbum);

                            Console.WriteLine("SUCCESS 1");
                        }
                        Console.WriteLine("SUCCESS 2");
                    } // ------------------------------ Exception
                    Console.WriteLine("SUCCESS 3");
                }
                catch (Exception any)
                {
                    Console.WriteLine("------------------------------------------");
                    Console.WriteLine("GenerateClassifiedLibrary: A more fatal Erroar reading file: " + currentFile.Name);
                    Console.WriteLine("Exception: " + any.ToString());
                    Console.WriteLine("------------------------------------------");
                }
            }

            // Generating classifiedLibrary done.
            Log("Generated Library Contents: " + classifiedLibrary.Count + " Albums in classifiedLibrary");
        }

        private void Log(String line)
        {
            RtbLogs.Text += "\n" + line;
        }

    }
}

这里是 专辑 对象的来源:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Music_Metadata_Experiments
{
    public class Album
    {
        public String Name
        {
            get; set;
        }

        public List<TagLib.File> Tracks
        {
            get; set;
        }

        public Album(String name, List<TagLib.File> tracks)
        {
            Name = name;
            Tracks = tracks;
        }

        public bool Contains(String trackTitle)
        {
            try
            {
                foreach (TagLib.File track in Tracks)
                {
                    if (track.Tag.Title == trackTitle)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("------------------------------------------");
                Console.WriteLine("Exception occured while checking Album.Contains(" + trackTitle + ")");
                Console.WriteLine(e.Message);
                Console.WriteLine("------------------------------------------");
                return false;
            }
            return false;
        }
    }
}

控制台输出示例

Album: Holz (Weihnachtslied) - Single
Track: Curse My Name
SUCCESS 1
SUCCESS 2
Ausnahme ausgelöst: "System.InvalidOperationException" in mscorlib.dll
------------------------------------------
GenerateClassifiedLibrary: A more fatal Erroar reading file: C:\Users\XXX\Music\iTunes\iTunes Media\Music\Blind Guardian\At the Edge of Time-05 Curse My Name.mp3
Exception: System.InvalidOperationException: Die Sammlung wurde geändert. Der Enumerationsvorgang kann möglicherweise nicht ausgeführt werden.
   bei System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   bei System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   bei System.Collections.Generic.List`1.Enumerator.MoveNext()
   bei Music_Metadata_Experiments.Form1.GenerateClassifiedLibrary(List`1 files) in C:\Users\XXX\Documents\Projekte\Music Metadata Experiments\Music Metadata Experiments\Form1.cs:Zeile 152.
------------------------------------------

A Screenshot of what's happening.

顺便说一句:我是 C# 新手

--

编辑:它现在按预期工作。 我还将应用程序设置为 x64 以启用更大音乐库的处理。

更新项目:https://1drv.ms/u/s!AphAKlhyqhP4g2eEk-3aqLeifo89?e=ACkb6w

So there error is key here: Exception: System.InvalidOperationException: Die Sammlung wurde geändert. Der Enumerationsvorgang kann möglicherweise nicht ausgeführt werden.

In English roughly translates to the following error message:

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

This means you are looping through a list, but you are modifying that list (e.g. adding or removing items) while you are looping through it.

Sample:

var lines = new List<string>();
lines.Add("hello");
lines.Add("world");
foreach(var line in lines) 
{
    lines.Add("adding a new item"); // Invalid operation
}

It's an invalid operation to add / remove an item from the list while you are looping through it.

In your case this is the culprit:

classifiedLibrary.Add(newAlbum); // << This is not valid inside a loop.
Console.WriteLine("SUCCESS 1");

如何解决这个问题? Simple - create an extra list that you add items to when they match a certain criteria, and don't use the list you are currently looping through.

Other tip:

I strongly advise to set your windows language to english. This will help you (as there are far more resources available on Whosebug with the right english translation).

InvalidOperation 似乎即将到来,因为您正试图在 foreach 循环中修改 classifiedLibrary。尝试更新您的 foreach 查找代码,如下所示:

// using System.Linq;

foreach (Album album in classifiedLibrary.ToList())

这样您将枚举 classifiedLibrary 集合的副本(而不是 classifiedLibrary 本身),这将允许您在循环中向 classifiedLibrary 添加一个项目。