Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

C#

Problem with objects.

I just realised that since objects are the same thing as variables, they too can only be used inside the the brackets where they were declared. This raised a pretty big issue for me: How can you effectively use the user's input to create a custom object?

For exemple, I am working on my Battleship program. The user has to type in the coordinates of his ships, but I ran into trouble at line 74 because when I finally created an instance of Ship, I was using it outside of where it was declared. Here's my code (sorry, didn't add comments yet):

using System;

namespace Battleship
{
  class Program
  {
    static void Main()
    {
      while(true)
      {
        //Variables
        int X;
        int Y;
        const int shipNumber = 3; 

        Console.Write("Enter any key or type \"quit\" to leave: ");
        var entry = Console.ReadLine();

        if(entry.ToLower() == "quit") {break;}

        //Player1 places his ships
        Console.Write("Player1 enter the coordinates of your ships(" + shipNumber + "):\n");

        for(int i = 0; i<shipNumber; i++)
        {
          while(true)
          {
          while(true)
          {
            Console.Write("X(#" + (i + 1) + "): ");
            entry = Console.ReadLine();
            try
            {
              X = (int.Parse(entry) - 1);
            }
            catch (FormatException)
            {
              Console.Write("Please enter a number");
              continue;
            }
            if(X>=MapLocation._width)
            {
              Console.Write("This number is outside the grid!\n");
              continue;
            }
            break;
          }
          while(true)
          {
            Console.Write("Y(#" + (i + 1) + "): ");
            entry = Console.ReadLine();
            try
            {
              Y = (int.Parse(entry) - 1);
            }
            catch (FormatException)
            {
              Console.Write("Please enter a number");
              continue;
            }
            if(Y>=MapLocation._height)
            {
              Console.Write("This number is outside the grid!\n");
              continue;
            }
            break;
          }
          if(i == 0)
          {
            new Ship(new MapLocation(X, Y), false);
          }
          else
          {
            foreach(Ship[] ship in ships)
            {
              if(MapLocation.DistanceTo(ship.X, ship.Y, X, Y) > 1)
              {
                 Console.Write("Ships must touch!\n");
                 continue;
              }
              else
              {
                 new Ship(new MapLocation(X, Y), false);
                 break;
              }  
            }
          }
          } 
        }



      }
    }
  } 
}

2 Answers

Steven Parker
Steven Parker
229,921 Points

Declare variables and objects in the outermost scope in which they are used.

This doesn't mean they have to be initialized when they are declared. You can declare empty objects and then give them contents in other scopes. For example, you could declare an empty array, then use code in a loop body (and an inner context) to add items to the array. But then outside of the loop the array along with it's new contents will be accessible.

Noob question here, how do I declared empty objects? I tried it before asking but it returned the compiler error "There is no overload of the method x that takes 0 parameters".

Steven Parker
Steven Parker
229,921 Points

It could be your object has no default constructor, so perhaps you can't have an "empty object" of that type. But you can declare an unassigned object variable in the outer scope. For example:

Ship myship;  // first, declare object variable without assigning
if (sometest)
{
  // in inner context, instantiate object and assign variable
  myship = new Ship(new MapLocation(X, Y), false);
}
// back in the outer context you can access the object's methods and properties
var myX = myship.X;

And how do you create a default constructor? I already have one that asks for a MapLocation and bool.

Also, I'm not sure I understand how to solve this. I tried adding "Ship ship;" at the beginning with the variables, but I got this compiler error at foreach loop:

"Game.cs(77,28): error CS0136: A local variable named ship' cannot be declared in thi s scope because it would give a different meaning toship', which is already used in a `parent or current' scope to denote something else"

Ship ship;

//Further down the code
if(i == 0)
          {
            ship = new Ship(new MapLocation(X, Y), false);
          }
          else
          {
            foreach(Ship[] ship in ships)
            {
              if(MapLocation.DistanceTo(ship.X, ship.Y, X, Y) > 1)
              {
                 Console.Write("Ships must touch!\n");
                 continue;
              }
              else
              {
                 new Ship(new MapLocation(X, Y), false);
                 break;
              }  
            }
          }
Steven Parker
Steven Parker
229,921 Points

That snippet doesn't include the cause of the problem. To enable a complete analysis, please make a snapshot of your workspace and post the link to it here.

Also, your loop is creating Ship objects but not storing them anywhere. Are you missing an assignment?

https://w.trhou.se/s7jph31laz

And I thought it was going through each Ship object in the array?

Steven Parker
Steven Parker
229,921 Points

I don't see any array. But the reason the compiler objects to the use of "ship" in the forEach loop on line 77 is that it was already declared as a "Ship" object on line 17. That same loop references "ships" but it doesn't seem to be defined anywhere. Did you mean to create an array of "ships" on line 17 instead?

Also, I noticed a few other potential issues. For example, at line 27:

        for(int i = 0; i<shipNumber; i++)
        {
          while(true)
          {
          while(true)
          {

I didn't see any obvious way to get out of the middle of the three nested loops.

On line 73 you assign the individual "ship", but shouldn't that be part of an array or list?

At line 79 you use Maplocation.DistanceTo as if it were a static method, but it's not definfed that way.

At line 88 you create a new Ship, but you don't assign it to anything.

I'm sure you'll find a few more things to work on also, but those stood out at first glance.

This is my first program since the end of C# Object, and I don't understand everything, that's why I'm doing this program.

I want the ships to touch when the player places them. So, what I'm trying to do is look at every Ship and see if this new MapLocation touches at least one of them. I thought an array was created whenever an object was created, but that might be wrong, again I'm a little hazy on the details.

Line 27: That is just lazy design. I don't know if the continue statements restarts the loop at the current i value or if it would jump to the next value. I was eager to start coding, the while is just a place holder, a safety measure.

Line 73: Same issue as the first one, I thought all objects of the same type were automatically in an array of that type.

Line 79: I thought for static method you didn't need the prefix (MapLocation) and with "non-static" you did. Again, there are loads of details I don't remember, how do I use it properly then?

Line 88: I give it a MapLocation and it's bool value, what do you mean by not assigning it to owt?

There will be a mery load of things to improve that's for sure. Again, this is my first program using objects and because of the way the teacher works I was lost most of the course, so the task seemed... daunting. But I'm confident once I get over this inital bump I'll be able to code through the other 2 programs easily. Thanks for the help mate.

Steven Parker
Steven Parker
229,921 Points

New objects only go into an array if you explicitly store them in one.

Static methods use the class name as the prefix, instance methods use the instance variable name as the prefix.

And about line 88:
    new Ship(new MapLocation(X, Y), false); :point_left: this creates a new Ship*, but does not store it anywhere
    MyShipArray[n] = new Ship(new MapLocation(X, Y), false); :point_left: so I would expect something like this instead

Thanks! I followed your instructions and it works now! I know it must have taken you a lot of time to look at my code and think of your reply, and I wanted you to know that it didn't fly under the radar. Thanks a lot again.