コードで設定された後でも、プロパティはnullです
-
03-07-2019 - |
質問
私はこれを長い間(3日間)解決しようとしてきましたが、それを理解することはできません。この問題はもう少し複雑なので、包括的に説明しようとします。
私の学校の課題は、C#Visual Studio 2008でOOPを使用して簡単なテキストゲームを作成することです(教師が提供してくれたライブラリに構築する必要があります)。コンソールのみを使用する必要があります。 PHPとC ++からのOOPにはかなりの経験がありますが、これを理解することはできません。
テキストゲームの80%はすでに動作しているので、既に動作し、問題に関係のないクラスやものに飽きさせません。では始めましょう:
ゲーム内の各コマンド(コンソールに入力してEnterキーを押すことができるもの)は、ゲームを構築するライブラリの抽象クラスとインターフェースの両方を拡張する単一のクラスで表されます。 Bellowは、アイテムを使用するためのコマンドを表すUseクラスです(たとえば、コンソールに「use sword」と入力すると、ゲームはswordというアイテムを探してuseメソッドを呼び出します):
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;
}
}
}
使用される他の2つのクラスがあります。 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日前にこの問題に遭遇したとき、ほとんどのことはスムーズに進み、それ以来、この問題を回避しようと試みてきました。
解決
あなたの問題は「新しい」キーワードにあります。 「使用」クラスで使用する場所は次のとおりです。
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);
次の出力が得られます。
direct:abcd
ACommand:wxyz
おそらく「Params」の定義をUseから完全に削除し、ACommandから定義を継承するだけです。おそらく名前と説明からも同様ですが、それが必要かどうかはここから理解できるはずです。
他のヒント
ACommandクラスのコードは表示されません..." new"を削除してみてください。 UseクラスのParams宣言の演算子。 property c.Params = command;を設定するとき実行メソッドで、base.Paramsの代わりにthis.parametersをチェックして、基本クラスのプロパティを実際に設定しています。
//これは単なる問題です // parametersプロパティを使用します。コマンドがあるはずです
//コンソールに入力しましたが、常にnull
//このメソッドの本体はまだコーディングしていないことに注意してください。
//問題を解決したらそれを行います。
これは、プロパティでnewを宣言したことが原因です。これらはオーバーライドするか、ACommandのロジックを変更する必要がない場合はまったく含めないでください。
ACommandとして参照する場合:
TextGame.Commands.ACommand c = parser.GetCommand(command);
c.Params = command;
ACommandのParamsまたはオーバーライド(定義済みの場合)を使用します。
新しいParamsはACommandのParamsをシャドウし、参照がUseCommandである場合にのみアクセスできます。
あなたの問題はここにあります:
private string parameters;
public new string Params
{
set { this.parameters = value; }
get { return this.parameters; }
}
コード内:
c.Params = command;
タイプ TextGame.Commands.ACommand
を参照しています。サブクラスでParamプロパティを非表示にしているため、非多態的な参照が発生しています。上記の定義を削除し、Paramの基本クラス定義に依存すれば、問題ありません。
この問題に遭遇してからしばらく経ちましたが、それをReflectorで開くと、ベースタイプに明示的にバインドされたcallvirtの背後にUse.Paramsプロパティが隠れていることがわかります。より速いタイピストが指摘したように。