views:

288

answers:

5

Hello,

I've been trying to solve this for ages (3 days) now and I just cannot figure it out. I will try to explain the problem comprehensively because it is a bit more complex.

My school assignement is to create a simple text game using OOP in C# Visual Studio 2008 (should be built on a library the teacher provided for us). It should only use console. I have a decent experience with OOP from PHP and C++ but I still cannot figure this out.

80% of the text game is already working so I won't bore you with classes and stuff that already works and is not related to the problem. Ok let's get started:

Each command in the game (what you can type into the console and hit enter) is represented by a single class both extending an abstract class and an interface from the library I am supposed to built the game on. Bellow is a class Use which represents a command for using items (e.g. you type "use sword" into the console and the game will look for an item called sword and call its use method):

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

There are two other classes that are used. The Parser class and the Game class. There are a bit longer so I will only post snippets of relevant stuff from them. Parser class:

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

Now a snippet from the Game class where I'm using both above classes:

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

I have added comments to the snippets to describe where is the problem. I hope I have explained it well.

Anyone has any ideas? I've been working on this projects for about 3 weeks now and most of the stuff went smoothly when 3 days ago I came across this problem and since then I've been trying to get my head around this problem.

Any help would be appreciated.

Regards, Richard

+9  A: 

Your problem is with the 'new' keyword. Here's where you're using it in the 'Use' class:

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

You're creating a different property that just happens to have the same name as a property on the type you are inheriting from. The 'new' keyword tells the compiler you meant to do that.

Basically, this means that if you do the following:

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

You'll get this output:

direct: abcd

ACommand: wxyz

You probably want to remove the definition of 'Params' entirely from Use and just inherit the one from ACommand. Probably from Name and Description as well, but you should be able to figure out from here if you want that or not.

Jonathan
You *just* beat me to it :)
Jon B
Thank you so much, it's exactly how you described. Problem solved :DThank you Sir.
Richard Knop
+2  A: 

// 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.

This is caused by you declaring new on your properties. These should be override, or not included at all if you don't need to change the logic of ACommand.

When you reference as an ACommand:

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

You will use either ACommand's Params, or your overrides (if you had defined one).

Your new Params shadow ACommand's Params, and are only accessible if your reference is a UseCommand.

Mark Brackett
+3  A: 

Without seeing the code for the ACommand class... Try removing the "new" operator in the Params declaration of the Use class. When your setting the property c.Params = command; is actually setting the property of the base class, in the Execute method your checking this.parameters instead of base.Params.

+1  A: 

It's been a while since I ran into this problem, but if you open that up in Reflector I expect you will see that you are hiding the Use.Params property behind a callvirt explicitly bound to its base type there.... as the faster typists pointed out.

Tetsujin no Oni
curious why the downvote, when I provided similar information, and a technique for examining the code to confirm it, to the upvoted answers above.
Tetsujin no Oni
I didn't vote, but I'll guess--redundancy?
Bill K
That wasn't me. I just voted your asnwer up. Not sure who voted it down.
Richard Knop
I downvoted, because I don't think it's reasonable to expect an admitted newbie to understand what you mean by "...hiding the User.Params property behind a callvirt explicitly bound to its base type...", nor how to divine that from MSIL. Not to mention that reading MSIL for this is a profound waste of time - the language is behaving as designed, he just needs to understand the language.
Mark Brackett
+2  A: 

Your problem is here:

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

In your code:

c.Params = command;

you are referencing the type TextGame.Commands.ACommand. Because you're hiding the Param property in your subclass, you're causing a non-polymorphic reference. Remove the definition above and rely on the base class definition of Param, and you'll be fine.

Michael Meadows