Свойство имеет значение null, даже после установки в коде - PullRequest
5 голосов
/ 01 мая 2009

Я пытался решить эту проблему целую вечность (3 дня), и я просто не могу понять это. Я постараюсь объяснить проблему комплексно, потому что она немного сложнее.

Моим школьным заданием является создание простой текстовой игры с использованием ООП в C # Visual Studio 2008 (должна быть построена на библиотеке, которую нам предоставил учитель). Следует использовать только консоль. У меня есть приличный опыт работы с ООП из PHP и C ++, но я до сих пор не могу понять это.

80% текстовой игры уже работает, поэтому я не буду утомлять вас классами и тем, что уже работает и не связано с проблемой. Хорошо, начнем:

Каждая команда в игре (которую вы можете набрать в консоли и нажать Enter) представлена ​​одним классом, расширяющим абстрактный класс и интерфейс из библиотеки, на которой я должен был построить игру. Сильфон - это класс Use, который представляет команду для использования предметов (например, вы вводите «use sword» в консоль, и игра ищет предмет под названием меч и вызывает метод его использования):

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

namespace Game.Commands
{
    class Use : TextGame.Commands.ACommand, TextGame.Commands.ICommand
    {
        private string name;
        public new string Name
        {
            set { this.name = value; }
            get { return this.name; }
        }

        private string description;
        public new string Description
        {
            set { this.description = value; }
            get { return this.description; }
        }

        private string parameters;
        public new string Params
        {
            set { this.parameters = value; }
            get { return this.parameters; }
        }

        public Use(string name, string description) : base(name, description)
        {
            this.name = name;
            this.description = description;
        }

        private TextGame.Core.GameState gameState;
        public TextGame.Core.GameState Execute(TextGame.Core.IGame game)
        {
            // This is just a test because it appears the problem is
            // with the parameters property. There should be a command
            // you have typed in the console but its always null
            // Note that I have not yet coded the body of this method.
            // I will do that once I solve the problem.
            if (this.parameters == null)
            {
                Console.WriteLine("is null");
            }
            else
            {
                Console.WriteLine(this.parameters);
            }
            return this.gameState;
        }
    }
}

Есть два других класса, которые используются. Класс Parser и класс Game. Они немного длиннее, поэтому я буду публиковать только фрагменты соответствующих материалов. Класс парсера:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections; // ArrayList, Dictionary, Hashtable
using System.Text.RegularExpressions; // regex engine
using Game.Commands;

namespace Game
{
    class Parser
    {
        private ArrayList commands = new ArrayList();

        // All commands that are available in the game so far are
        // initialized here in the constructor (and added to the arraylist)...
        // skip to the other method this is not important
        public Parser()
        {
            this.commands.Add(new North("^north", "Go north"));
            this.commands.Add(new South("^south", "Go south"));
            this.commands.Add(new East("^east", "Go east"));
            this.commands.Add(new West("^west", "Go west"));
            this.commands.Add(new Use("^use\\s\\w+", "Try to use the selected item"));
            this.commands.Add(new Quit("^quit", "Quit the game"));
        }

        // This method takes as an argument a string representing
        // a command you type in the console. It then searches the arraylist
        // via the regex. If the command exists, it returns an the command object
        // from the arraylist
        // This works fine and returns right objects (tested)
        public TextGame.Commands.ACommand GetCommand(string command)
        {
            TextGame.Commands.ACommand ret = null;
            foreach (TextGame.Commands.ACommand c in this.commands)
            {
                Regex exp = new Regex(@c.Name, RegexOptions.IgnoreCase);
                MatchCollection MatchList = exp.Matches(command);
                if (MatchList.Count > 0)
                {
                    ret = c;
                }
            }
            return ret;
        }
    }
}

Теперь фрагмент из класса Game, где я использую оба вышеупомянутых класса:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TextGame.Core;
using System.Collections;
using Game.Items;
using Game.Commands;

namespace Game
{
    class Game : TextGame.Core.IGame
    {

        public void Play()
        {   
            // Here I read commands from the console in a loop and
            // call the ProcessCommand() method. No problem here.
            while (true)
            {
                string command = Console.ReadLine();
                this.ProcessCommand(command);
            }
        }

        // This is the IMPORTANT method so take a closer look
        private TextGame.Core.GameState gameState;
        public TextGame.Core.GameState ProcessCommand(string command)
        {
            Parser parser = new Parser();
            TextGame.Commands.ACommand c = parser.GetCommand(command);
            if (c != null)
            {
                // HERE I ADD THE COMMAND FROM THE CONSOLE TO THE C OBJECT
                // I ADD IT VIA THE SETTER TO THE PARAMETERS PROPERTY
                // OF THE COMMAND
                c.Params = command;
                // AND I CALL THE COMMAND'S EXECUTE() METHOD - SEE THE FIRST CLASS -
                // USE - WHERE I TEST FOR THE PARAMS PROPERTY BUT IT IS STILL NULL
                this.gameState = ((TextGame.Commands.ICommand)c).Execute(this);
            }
        }
    }
}

Я добавил комментарии к фрагментам, чтобы описать, в чем проблема. Надеюсь, я хорошо это объяснил.

У кого-нибудь есть идеи? Я работаю над этими проектами около 3 недель, и большинство вещей прошло гладко, когда 3 дня назад я столкнулся с этой проблемой, и с тех пор я пытался разобраться с этой проблемой.

Ответы [ 5 ]

9 голосов
/ 01 мая 2009

Ваша проблема с ключевым словом "new". Вот где вы используете его в классе «Использование»:

    private string parameters;
    public new string Params
    {
        set { this.parameters = value; }
        get { return this.parameters; }
    }

Вы создаете другое свойство, имя которого совпадает с именем свойства типа, от которого вы наследуете. Ключевое слово «new» сообщает компилятору, что вы хотели это сделать.

По сути, это означает, что если вы делаете следующее:

var x = new Use();
x.Params = "abcd";
((ACommand)x).Params = "wxyz";
Console.Writeline("direct: " + x.Params);
Console.Writeline("ACommand: " + ((ACommand)x).Params);

Вы получите этот вывод:

прямой: abcd

ACommand: wxyz

Вы, вероятно, хотите полностью удалить определение «Params» из Use и просто наследовать определение от ACommand. Вероятно, из Имени и Описания, но вы должны быть в состоянии выяснить отсюда, хотите вы этого или нет.

3 голосов
/ 01 мая 2009

Не видя код для класса ACommand ... Попробуйте удалить оператор "new" в объявлении Params класса Use. Когда вы устанавливаете свойство c.Params = команда; фактически устанавливает свойство базового класса, в методе Execute проверяется this.parameters вместо base.Params.

2 голосов
/ 01 мая 2009

Ваша проблема здесь:

private string parameters;
public new string Params
{
    set { this.parameters = value; }
    get { return this.parameters; }
}

В вашем коде:

c.Params = command;

вы ссылаетесь на тип TextGame.Commands.ACommand. Поскольку вы скрываете свойство Param в своем подклассе, вы вызываете неполиморфную ссылку. Удалите приведенное выше определение и положитесь на определение базового класса Param, и все будет хорошо.

2 голосов
/ 01 мая 2009

// Это всего лишь тест, потому что, похоже, проблема в
// со свойством параметров. Там должна быть команда
// вы ввели в консоль, но она всегда пуста
// Обратите внимание, что я еще не закодировал тело этого метода.
// Я сделаю это, как только решу проблему.

Это вызвано тем, что вы объявили новое в своих свойствах. Они должны быть переопределены или не включены вообще, если вам не нужно менять логику ACommand.

Когда вы ссылаетесь как ACommand:

TextGame.Commands.ACommand c = parser.GetCommand(command);            
c.Params = command;

Вы будете использовать либо параметры ACommand, либо ваши переопределения (если вы их определили).

Ваши новые параметры скрывают параметры ACommand и доступны только в том случае, если ваша ссылка - UseCommand.

1 голос
/ 01 мая 2009

Прошло много времени с тех пор, как я столкнулся с этой проблемой, но если вы откроете это в Reflector, я ожидаю, что вы увидите, что скрываете свойство Use.Params за callvirt, явно привязанным к его базовому типу .... как указали более быстрые машинистки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...