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

时间:2022-09-06 20:24:44

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.

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

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.

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

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:

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

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):

游戏中的每个命令(你可以输入到控制台并点击回车)都由一个类表示,它既扩展了抽象类,又从我应该构建游戏的库中扩展了接口。 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;
        }
    }
}

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:

还有两个使用的类。 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;
        }
    }
}

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

现在是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);
            }
        }
    }
}

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.

有人有什么想法吗?我已经在这个项目上工作了大约3个星期,大部分时间都很顺利,3天前我遇到了这个问题,从那以后我一直试图解决这个问题。

5 个解决方案

#1


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.

您正在创建一个不同的属性,恰好与您继承的类型上的属性具有相同的名称。 'new'关键字告诉编译器你打算这样做。

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.

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

#2


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.

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

#3


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

//这只是一个测试,因为看起来问题是//带有parameters属性。应该有一个命令//你已经在控制台中键入但它总是为空//注意我还没有编码这个方法的主体。 //一旦我解决了问题,我会这样做

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.

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

When you reference as an ACommand:

当您作为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).

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

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

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

#4


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.

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

#5


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.

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

#1


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.

您正在创建一个不同的属性,恰好与您继承的类型上的属性具有相同的名称。 'new'关键字告诉编译器你打算这样做。

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.

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

#2


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.

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

#3


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

//这只是一个测试,因为看起来问题是//带有parameters属性。应该有一个命令//你已经在控制台中键入但它总是为空//注意我还没有编码这个方法的主体。 //一旦我解决了问题,我会这样做

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.

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

When you reference as an ACommand:

当您作为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).

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

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

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

#4


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.

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

#5


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.

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