简体   繁体   中英

Property is null, even after being set in code

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 assignment 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 (eg 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.

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.

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.

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

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.

我遇到这个问题已经有一段时间了,但如果你在Reflector中打开它,我希望你会看到你隐藏了一个callvirt后面的Use.Params属性,显然绑定到它的基类型....打字员指出。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM