即使在代码中设置后,属性也为null

时间:2009-05-01 19:07:31

标签: c# visual-studio oop console

我一直试图解决这个问题很长时间(3天)现在我无法解决这个问题。我将尝试全面解释这个问题,因为它有点复杂。

我的学校任务是在C#Visual Studio 2008中使用OOP创建一个简单的文本游戏(应该建立在教师为我们提供的库上)。它应该只使用控制台。我在使用PHP和C ++的OOP方面有很好的经验,但我仍然无法理解这一点。

80%的文字游戏已经开始运作,所以我不会厌倦你已经有效的课程和与问题无关的课程。好的,让我们开始吧:

游戏中的每个命令(你可以键入控制台并点击回车)由一个类表示,它既扩展了抽象类,又从我应该构建游戏的库中扩展了接口。 Bellow是一个类Use用于表示使用项目的命令(例如,您在控制台中输入“use sword”,游戏将查找名为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 个答案:

答案 0 :(得分:9)

您的问题在于'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

您可能希望完全从Use中删除'Params'的定义,并且只是继承ACommand中的那个。可能也来自名称和描述,但你应该能够从这里弄清楚你是否想要这个。

答案 1 :(得分:3)

没有看到ACommand类的代码...尝试删除Use类的Params声明中的“new”运算符。当你设置属性c.Params = command;实际上是在设置基类的属性,在Execute方法中检查this.parameters而不是base.Params。

答案 2 :(得分:2)

  

//这只是一个测试,因为它似乎问题是   //使用parameters属性。应该有一个命令   //你已经输入了控制台,但它始终为空   //请注意,我还没有对此方法的主体进行编码   //一旦我解决问题,我会这样做。

这是由您在属性上声明新内容造成的。如果您不需要更改ACommand的逻辑,则应该覆盖这些,或者根本不包括它们。

当您作为ACommand引用时:

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

您将使用ACommand的Params或您的覆盖(如果您已定义了一个)。

您的新Params影子ACommand的Params,只有在您的引用是UseCommand时才可访问。

答案 3 :(得分:2)

你的问题在这里:

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

在您的代码中:

c.Params = command;

您正在引用类型TextGame.Commands.ACommand。因为您在子类中隐藏了Param属性,所以您将导致非多态引用。删除上面的定义并依赖Param的基类定义,你会没事的。

答案 4 :(得分:1)

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