Свойство имеет значение null даже после установки в коде.
-
03-07-2019 - |
Вопрос
Я пытаюсь решить эту проблему уже целую вечность (3 дня) и просто не могу этого понять.Я постараюсь объяснить проблему комплексно, потому что она немного сложнее.
Мое школьное задание — создать простую текстовую игру с использованием ООП в C# Visual Studio 2008 (должна быть построена на основе библиотеки, предоставленной нам учителем).Он должен использовать только консоль.У меня есть приличный опыт работы с ООП на PHP и C++, но я до сих пор не могу в этом разобраться.
80% текстовой игры уже работает, поэтому не буду утомлять вас классами и прочим, что уже работает и не имеет отношения к проблеме.Хорошо, давайте начнем:
Каждая команда в игре (то, что вы можете ввести в консоль и нажать Enter) представлена одним классом, одновременно расширяющим абстрактный класс и интерфейс из библиотеки, на которой я должен построить игру.Bellow — это класс Use, который представляет команду для использования элементов (например,вы вводите в консоль «использовать меч», и игра будет искать предмет с именем меч и вызывать его метод использования):
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 дня назад я столкнулся с этой проблемой, и с тех пор я пытаюсь разобраться в этой проблеме.
Решение
Ваша проблема связана с ключевым словом «новое».Вот где вы используете его в классе «Использовать»:
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
Команда:wxyz
Вероятно, вы захотите полностью удалить определение «Params» из Use и просто унаследовать его от ACommand.Вероятно, также из имени и описания, но отсюда вы сможете понять, хотите вы этого или нет.
Другие советы
Не видя кода класса ACommand...Попробуйте удалить оператор «новый» в объявлении Params класса Use.Когда вы устанавливаете свойство c.Params = команда;фактически устанавливает свойство базового класса, в методе Execute вы проверяете this.parameters вместо base.Params.
// Это всего лишь проверка, потому что похоже, что проблема в
// со свойством параметров.Должна быть команда
// вы набрали в консоли, но там всегда ноль
// Обратите внимание, что я еще не написал тело этого метода.
// Я сделаю это, как только решу проблему.
Это вызвано тем, что вы объявили что-то новое в своих свойствах.Их следует переопределить или вообще не включать, если вам не нужно изменять логику ACommand.
Когда вы ссылаетесь на ACommand:
TextGame.Commands.ACommand c = parser.GetCommand(command);
c.Params = command;
Вы будете использовать либо параметры ACommand, либо свои переопределения (если вы их определили).
Ваши новые параметры затеняют параметры ACommand и доступны только в том случае, если ваша ссылка является UseCommand.
Ваша проблема здесь:
private string parameters;
public new string Params
{
set { this.parameters = value; }
get { return this.parameters; }
}
В вашем коде:
c.Params = command;
вы ссылаетесь на тип TextGame.Commands.ACommand
.Поскольку вы скрываете свойство Param в своем подклассе, вы создаете неполиморфную ссылку.Удалите приведенное выше определение и положитесь на определение базового класса Param, и все будет в порядке.
Прошло много времени с тех пор, как я столкнулся с этой проблемой, но если вы откроете это в Reflector, я ожидаю, что вы увидите, что вы скрываете свойство Use.Params за callvirt, явно привязанным к его базовому типу....как отметили более быстрые машинистки.