C# TargetInvocationException и FormatException

Хорошо, у меня здесь действительно странная ситуация. Сначала мне нужно дать немного фона. Я создаю агентов ИИ для игры, созданной на движке XNA. То, как все устроено, предполагается, что люди используют инфраструктуру агента для создания.dll, которую игра затем использует для загрузки агентов при запуске.

У меня есть доступ к коду игры (чтобы я мог видеть, что происходит), и на данный момент я использую чужие агенты в качестве отправной точки для своих собственных. Недавно в игру было внесено несколько изменений (и, соответственно, в фреймворк), главным образом в названиях классов и интерфейсов, что означает, что я должен ускорить работу агентов. Итак, после того, как я сделал все обновления, необходимые для того, чтобы иметь возможность скомпилировать агенты с новой версией фреймворка, я столкнулся с проблемой. Это код для игры, загружающий.dll

 // dynamically load assembly from file GeometryFriendsAgents.dll
 Assembly agentsDLL = Assembly.LoadFile(path);

 // get type of classes BallAgent and SquareAgent from just loaded Assembly
 Type circleType = AgentsDLL.GetType("GeometryFriendsAgents.CircleAgent");
 Type rectangleType = AgentsDLL.GetType("GeometryFriendsAgents.RectangleAgent");
 try { 
     // create instances of classes BallAgent and SquareAgent
     npcCircle = (ICircleAgent)Activator.CreateInstance(circleType);
     npcRectangle = (IRectangleAgent)Activator.CreateInstance(rectangleType);
 }catch(TargetInvocationException e){
     throw e.InnerException;
 }

Я могу подтвердить, что путь правильный. Строки внутри try/catch вызовут исключение TargetInvocationException, когда я попытаюсь запустить игру (которая автоматически загрузит агентов). Я добавил try/catch, чтобы увидеть внутреннее исключение, которое является FormatException, и VisualStudio предоставляет дополнительную информацию о том, что входная строка была не в правильном формате.

Я не знаю, какая часть кода агента будет иметь отношение к этому, но я еще не дошел до странной части. В реализации, которую я использую, агенты используют класс LearningCenter. Этот класс по существу читает и записывает обучающие файлы агентов. в начале класса он хранит путь к файлам обучения:

protected const string path = @"..\..\..\..\Agents\";

Так что вот где все становится странным. Это правильный путь для учебных файлов. Когда раньше я допускал ошибку, у меня был этот путь (который раньше повторялся много раз по всему коду) как

protected const string path = @"..\..\..\..\Agents";

Когда я собираю.dll с неверным путем, я могу успешно загрузить агентов, и он запустит игру. Проблема в том, что путь неверный, и когда LearningCenter пытается записать учебный файл, он, очевидно, завершится с ошибкой DirectoryNotFoundException. Рассматриваемый метод:

public void EndGame(float knownStatesRatio) {
    if (_toSave) {
        FileStream fileStream = new FileStream(path + _learningFolder + "\\Ratios.csv", FileMode.Append);
        StreamWriter sw = new StreamWriter(fileStream);
        sw.WriteLine(knownStatesRatio);
        sw.Close();
        fileStream.Close();
        fileStream = new FileStream(path + _learningFolder + "\\IntraPlatformLearning.csv", FileMode.Create);
        DumpLearning(fileStream, _intraplatformPlayedStates);
        fileStream.Close();
        if (interPlatform) {
            fileStream = new FileStream(path + _learningFolder + "\\InterPlatformLearning.csv", FileMode.Create);
            DumpLearning(fileStream, _interplatformPlayedStates);
            fileStream.Close();
            }
        }
    }

Исключение возникает сразу при создании нового файлового потока. Я пытался сдвинуть пропавшие \ к _learningFolder переменная, но когда я делаю это возвращается к первой проблеме. Пока путь неверен, я могу запустить игру...

Я должен также упомянуть, что перед этим я первоначально столкнулся с другой исключительной ситуацией TargetInvocationException в том же месте. В то время проблема была решена путем изменения видимости классов агента на public.

Я понимаю, что вещь с путем, вероятно, скрывает реальную проблему, но я просто не знаю, где искать дальше.

редактировать: вот трассировка стека для первой проблемы

GeometryFriends.exe!GeometryFriends.AI.AgentsManager.LoadAgents() Line 396
GeometryFriends.exe!GeometryFriends.Levels.SinglePlayerLevel.LoadLevelContent() Line 78
GeometryFriends.exe!GeometryFriends.Levels.Level.LoadContent() Line 262
GeometryFriends.exe!GeometryFriends.ScreenSystem.ScreenManager.LoadContent() Line 253
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.DrawableGameComponent.Initialize() 
GeometryFriends.exe!GeometryFriends.ScreenSystem.ScreenManager.Initialize() Line 221 
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.Initialize() 
GeometryFriends.exe!GeometryFriends.Engine.Initialize() Line 203
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.RunGame(bool useBlockingRun)
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.Run()
GeometryFriends.exe!GeometryFriends.Program.Main(string[] args) Line 16

Первым ошибочным агентом является CircleAgent, вот конструктор:

public CircleAgent() {
    //Change flag if agent is not to be used
    SetImplementedAgent(true);

    lastMoveTime = DateTime.Now;
    lastRefreshTime = DateTime.Now;
    currentAction = 0;
    rnd = new Random(DateTime.Now.Millisecond);

    model = new CircleWorldModel(this);

    learningCenter = new CircleLearningCenter(model);
    learningCenter.InitializeLearning();

    startTime = DateTime.Now;
}

редактировать 2: Хорошо, мне удалось разделить на источник FormatException. Ошибка возникает в этом методе CircleLearningCenter (оператор в первом if):

public override void addStateMovementValue(string[] lineSplit, string stateId, ref Dictionary<string, Dictionary<int, double>> lessons) {
    if (!lineSplit[1].Equals("0")) {
        lessons[stateId].Add(Moves.ROLL_LEFT, double.Parse(lineSplit[1]));
    }
    if (!lineSplit[2].Equals("0")) {
        lessons[stateId].Add(Moves.ROLL_RIGHT, double.Parse(lineSplit[2]));
    }
    if (!lineSplit[3].Equals("0")) {
        lessons[stateId].Add(Moves.JUMP, double.Parse(lineSplit[3]));
    }
}

Который вызывается этим методом в LearningCenter:

private void createLearningFromFile(FileStream fileStream, ref Dictionary<string, Dictionary<int, double>> lessons) {
    lessons = new Dictionary<string, Dictionary<int, double>>();
    StreamReader sr = new StreamReader(fileStream);
    string line;
    while ((line = sr.ReadLine()) != null) {
        string[] lineSplit = line.Split(',');
        string stateId = lineSplit[0];
        lessons.Add(stateId, new Dictionary<int, double>());
        addStateMovementValue(lineSplit, stateId, ref lessons);
    }
}

который в свою очередь вызывается этим методом (который вызывается в конструкторе круга):

public void InitializeLearning() {
    if (File.Exists(Path.Combine(Path.Combine(path, _learningFolder), "IntraPlatformLearning.csv"))) {
        FileStream fileStream = new FileStream(Path.Combine(Path.Combine(path, _learningFolder),"IntraPlatformLearning.csv"), FileMode.Open);
        createLearningFromFile(fileStream, ref _intraplatformLessonsLearnt);
        fileStream.Close();
    } else {
        createEmptyLearning(ref _intraplatformLessonsLearnt);
    }
    if (File.Exists(Path.Combine(Path.Combine(path, _learningFolder), "InterPlatformLearning.csv"))) {
        FileStream fileStream = new FileStream(Path.Combine(Path.Combine(path, _learningFolder), "InterPlatformLearning.csv"), FileMode.Open);
        createLearningFromFile(fileStream, ref _interplatformLessonsLearnt);
        fileStream.Close();
     } else {
         createEmptyLearning(ref _interplatformLessonsLearnt);
     }
}

В случае, если это не очевидно, CircleLearningCenter является подклассом LearningCenter. Кроме того, извините за текстовую стену, но я в своем уме.

1 ответ

Используйте System.IO.Path.Combine() для совмещения частей пути. Например:

вместо:

FileStream(path + _learningFolder + "\\Ratios.csv")

использовать:

FileStream(Path.Combine(Path.Combine(path , _learningFolder) , "Ratios.csv"))

Только не забудьте удалить \\ из каждой части. И сделать то же самое для других путей FileStream.

Другие вопросы по тегам