my program - erroneous?

Started by
4 comments, last by Zahlman 17 years, 1 month ago
I started a small program last night, just to test a few things out, and im getting a bunch of errors: "error CS0120: An object reference is required for the nonstatic field, method, or propery of" <that error> + myTextGame.me <that error> + myTextGame.doQuit <that error> + myTextGame.processCmd(string) It consists of 2 files: myTextGame.cs, and TextCharacter.cs. Heres what a few variables/functions mean (since i didnt comment the program): myTextGame: doQuit = to see if the user wants to quit processCmd(string) = take the user's input and see what they want to do me = TextCharacter instance TextCharacter: (excuding the large number of variables) GetHealth(int) = returns the health (max, current, or percent, depending on int) GetName() = returns the name GetClassID() = returns the int version of the player's character class GetClass() = returns the string version of the player's character class

//myTextGame.cs
using System;

class myTextGame
{
	TextCharacter me;
	bool doQuit;
	
	public static void Main()
	{
		doQuit=false;
		Console.Write("Enter your name: ");
		me=new TextCharacter(Console.ReadLine(),TextCharacter.MAGE);
		Console.WriteLine("Name: "+me.GetName()+" Class: "+me.GetClass());
		
		//game loop
		while(me.GetHealth(TextCharacter.CURRENT)>0||doQuit==true)
		{
			Console.Write("Enter a command: ");
			processCmd(Console.ReadLine());
			Console.WriteLine("doQuit: "+doQuit);
		}
	}
	public void processCmd(string command)
	{
		if(command=="help")
		{
			
		}
		if(command=="quit"||command=="exit")
			doQuit=true;
		if(command=="health")
			Console.WriteLine("Health: "+me.GetHealth(TextCharacter.MAX)+"/"+me.GetHealth(TextCharacter.CURRENT)+" ("+me.GetHealth(TextCharacter.PERCENT)+"%)");
		if(command=="class")
			Console.WriteLine("Class: "+me.GetClass());
	}
}


//TextCharacter.cs
class TextCharacter
{
	public const int	CURRENT=0,
										MAX=1,
										PERCENT=2;
	public const int	MAGE=0,
										WARRIOR=1,
										ARCHER=2;
	private string name;
	private int cclass;
	private int max_health,cur_health;
	private const int	MAGE_DEFAULT_HEALTH=10,
										WARRIOR_DEFAULT_HEALTH=20,
										ARCHER_DEFAULT_HEALTH=14;
	
	public TextCharacter(string oName,int ocClass)
	{
		this.name=oName;
		this.cclass=ocClass;
		if(this.cclass==MAGE)
		{
			this.max_health=MAGE_DEFAULT_HEALTH;
		}
		if(this.cclass==WARRIOR)
		{
			this.max_health=WARRIOR_DEFAULT_HEALTH;
		}
		if(this.cclass==ARCHER)
		{
			this.max_health=ARCHER_DEFAULT_HEALTH;
		}
		this.cur_health=this.max_health;
	}
	public int GetHealth(int which)
	{
		if(which==CURRENT)
			return this.cur_health;
		if(which==MAX)
			return this.max_health;
		if(which==PERCENT)
			return (100/(this.max_health/this.cur_health));
		return 0;
	}
	public string GetName()
	{
		return this.name;
	}
	public int GetClassID()
	{
		return this.cclass;
	}
	public string GetClass()
	{
		string sTemp;
		switch(this.cclass)
		{
			case MAGE: sTemp="Mage";break;
			case WARRIOR: sTemp="Warrior";break;
			case ARCHER: sTemp="Archer";break;
			default: sTemp="NOCLASS";break;
		}
		return sTemp;
	}
}

I was trying to figure it out last night for about 45 mins, searching through a few books i have. I dont have a clue why it wont work. PS. When i compile(or what it called), heres what i type: csc /nologo /out:myTextGame.exe myTextGame.cs TextCharacter.cs
Advertisement
The error is pretty explicit: main is a static function, so it cannot access non-static members of the class. When it is called, no instance of the class is created, so there is no this object through which to access the members. If you wish to call the member functions of that class, you should create a new instance to call them on.
Thank you so much! I fixed it and it runs now.
To go into more detail: members (both data members and functions) which are "static" are shared by all instances; the class basically just acts as a namespace for them. Whereas those which are not static belong to each instance of the class.

In Main(), you write the code where the program starts. The idea is that right at the start of the program, there are no myTextGame objects anywhere, just the myTextGame class. Thus you have to start out in a static function, since these don't require an instance of the class to operate.

Of course, because static functions don't "belong to" any particular instance, they can't access the members of a particular instance. So when you declare 'me' and 'doQuit' as non-static members, the compiler complains: "uh, the me and doQuit members of which object, exactly?"



To get around this, OO purists often suggest that you avoid static stuff as much as possible, because it is "viral" (trying to fix things by making them static ends up producing lots of static stuff, and you lose reusability because you can't instantiate multiple copies of those variables - they all get shared). To minimize the impact of static stuff, the usual recommended way is to create an object right away and run one of its methods, which does the work that Main would have. This would be a local variable within Main (because nothing else needs access to it) and we don't actually need to assign it to a variable, because we'll just use it once and throw it away (let it be garbage collected).

//myTextGame.csusing System;class myTextGame{	TextCharacter me;	bool doQuit;		public static void Main()	{		new myTextGame().run();	}	public void run()	{		// as before	}	public void processCmd(string command)	{		// as before	}}
Thanks for the input. I noticed you mentioned "namespace" earlier. I read about namespaces in a few different references, but im still confused about them. Could you explain to me what they are used for and why i need it?
A namespace is exactly what it sounds like: a space in which names are found.

The idea is to avoid collisions between names for things (typically variables; but we could also be talking about function names, class names, type names...). When two things have the same name, the compiler tends to get confused and/or complain (after all, it has no "intelligence" and cannot read your mind).

In the old days with more primitive languages like C, we used to avoid this by having conventions for how things were names. So for example, everything "belonging" to some module might have some prefix on its name indicating the module - so we might have 'my_widget' and 'your_widget' classes for example. This ends up producing a lot of ugly and redundant-looking code, because the prefixes are part of the names themselves, and therefore have to be typed every time. That means for example that when I'm writing 'my' code, there are my_ prefixes everywhere. This is perverse; when I write 'my' code, things should be assumed to be 'my' things by default, but there is no 'default'.

Enter namespaces. When we place a name into a namespace, it becomes different from the same name in a different namespace. When the compiler tries to figure out what something means, it has some specific rules for deciding which name is meant - typically, it will first try to consider the name as belonging to the namespace that is the "current context", and then look into the "global" namespace if the current one doesn't contain anything with that name.

So now, when I write 'my' code, I wrap it in some markers indicating that the code is in the "context" of 'my' namespace, and all the variables etc. are looked up first in 'my' namespace and then in the "global" one (i.e. the set of all things not explicitly put into any namespace). If I need to look at one of 'your' variables, or create an instance of 'your' widget, then I explicitly qualify my request, by asking for a 'your::widget' (in C++).

Finally, for convenience, namespaces can typically be "used", in the sense that you specify "also try this namespace for looking up anything in the following code", or if you want to be more refined, "also try this namespace to look up this name in the following code". That lets you save typing if 'your' module uses lots of 'my' stuff, and it happens that you know there won't be any naming collisions in the bit of code you're currently writing.

In C++, the standard library puts all its stuff into a namespace named 'std', to protect you from accidentally referring to something. Which if a good thing, because there are lots of things in the standard library with names that are easy to ask for accidentally - for example, there is a function std::distance.

In Java, there is no explicit support for namespaces, but you can simulate them by using static members of classes (at least for variables and functions). You see, since there will only be one copy of such a variable, and such a function can operate independantly of an object, they become like globals - except that from outside the class, you have to specify which class to look in to find those static members, and *inside* the class, you don't (because the current class is automatically part of the current context for looking up names). This provides only the basic features, but it counts as a sort of namespacing. And in fact, the Java standard library uses this technique: there are some math functions that are static functions of the class java.util.Math - this class is designed so that you can't create instances of it; it just exists so that you can write things like 'Math.cos(x)', where the class is acting as a namespace for the cos() function.

C# has real namespaces (to the best of my knowledge), so you should prefer those. But it's also useful to understand namespaces as a way of understanding static members. (In your code, if I understand what I've read of C# correctly, you are making use of the "using another namespace" feature with the 'using System;' statement - this is causing the name 'Console' to be looked up in the System module, which is good, because it wouldn't be found if the compiler only looked in class myTextGame, as it would otherwise.)

This topic is closed to new replies.

Advertisement