Pregunta

He estado tratando de resolver esto por años (3 días) ahora y simplemente no puedo resolverlo. Intentaré explicar el problema de manera integral porque es un poco más complejo.

Mi tarea escolar es crear un juego de texto simple usando OOP en C # Visual Studio 2008 (debe estar construido en una biblioteca que el maestro nos proporcionó). Solo debe usar la consola. Tengo una experiencia decente con OOP de PHP y C ++ pero todavía no puedo resolver esto.

El 80% del juego de texto ya está funcionando, por lo que no te aburriré con clases y cosas que ya funcionan y no están relacionadas con el problema. Ok vamos a empezar:

Cada comando en el juego (lo que puedes escribir en la consola y presionar enter) está representado por una sola clase que extiende una clase abstracta y una interfaz desde la biblioteca en la que se supone que debo construir el juego. Bellow es un uso de clase que representa un comando para usar elementos (por ejemplo, escribe " use espada " en la consola y el juego buscará un elemento llamado espada y llamará a su método de uso):

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;
        }
    }
}

Hay otras dos clases que se utilizan. La clase Parser y la clase Game. Hay un poco más de tiempo, así que solo publicaré fragmentos de cosas relevantes de ellos. Clase de analizador:

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;
        }
    }
}

Ahora, un fragmento de la clase Game donde estoy usando las dos clases anteriores:

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

He agregado comentarios a los fragmentos para describir dónde está el problema. Espero haberlo explicado bien.

¿Alguien tiene alguna idea? He estado trabajando en estos proyectos durante aproximadamente 3 semanas y la mayoría de las cosas se resolvieron sin problemas cuando hace 3 días me encontré con este problema y desde entonces he estado tratando de entender este problema.

¿Fue útil?

Solución

Su problema es con la palabra clave 'nuevo'. Aquí es donde lo está utilizando en la clase 'Uso':

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

Estás creando una propiedad diferente que por casualidad tiene el mismo nombre que una propiedad en el tipo del que estás heredando. La palabra clave 'nuevo' le dice al compilador que querías hacer eso.

Básicamente, esto significa que si haces lo siguiente:

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

Obtendrás esta salida:

  

direct: abcd

     

Comando: wxyz

Probablemente desee eliminar la definición de 'Parámetros' completamente de Uso y simplemente heredar la de ACommand. Probablemente también de Nombre y Descripción, pero deberías poder averiguar desde aquí si quieres eso o no.

Otros consejos

Sin ver el código de la clase ACommand ... Intente eliminar el " nuevo " Operador en la declaración de parámetros de la clase de uso. Cuando está configurando la propiedad c.Params = comando; en realidad, está configurando la propiedad de la clase base, en el método Execute, estás comprobando this.parameters en lugar de base.Params.

  

// Esto es solo una prueba porque parece que el problema es
  // con la propiedad de los parámetros. Debe haber un comando
  // has escrito en la consola pero siempre es nulo
  // Tenga en cuenta que todavía no he codificado el cuerpo de este método.
  // Lo haré una vez que resuelva el problema.

Esto se debe a que declara nuevo en sus propiedades. Estos deben anularse, o no incluirse en absoluto si no necesita cambiar la lógica de ACommand.

Cuando hace referencia como ACommand:

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

Utilizará los parámetros de ACommand o sus anulaciones (si ha definido uno).

Sus nuevos parámetros se encuentran a la par de los parámetros de ACommand, y solo son accesibles si su referencia es un UseCommand.

Tu problema está aquí:

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

En tu código:

c.Params = command;

estás haciendo referencia al tipo TextGame.Commands.ACommand . Debido a que está ocultando la propiedad Param en su subclase, está causando una referencia no polimórfica. Elimina la definición anterior y confía en la definición de clase base de Param, y estarás bien.

Ha pasado un tiempo desde que encontré este problema, pero si lo abres en Reflector, espero que veas que estás ocultando la propiedad Use.Params detrás de un callvirt vinculado explícitamente a su tipo base allí ... como señalaron los mecanógrafos más rápidos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top