Hi I am a little new in programming in C#, and I am a little stuck. I have tried searching this site, but I have not been successful on finding an answer to my question. I have also tried changing my private to a public but that did not work.
Here is the error message I am getting:
Error 2 Inconsistent accessibility: parameter type 'exam2.location' is
less accessible than method
'exam2.Form1.MoveToANewLocation(exam2.location)'
Here is part of my code:
public Form1()
{
IntializeComponent();
CreateObject();
MoveToANewLocation(livingRoom);
}
private void MoveToANewLocation(location newLocation)
{
currentLocation = newLocation;
comboBox1.Items.Clear();
for (int i = 0; i < currentLocation.Exits.Length; i++)
{
comboBox1.Items.Add(currentLocation.Exits[i].Name);
comboBox1.SelectedIndex = 0;
}
textBox1.Text = currentLocation.Description;
if (currentLocation is IHasExteriorDoor)
{
GoThroughTheDoor.Visible = true;
}
else
{
GoThroughTheDoor.Visible = false;
}
}
abstract class location
{
public location(string name)
{
this.name = name;
}
public location[] Exits;
private string name;
public string Name
{
get { return name; }
}
public virtual string Description
{
get {
string description = "You're standing in the" + name +
". You see exits to the following places: ";
for (int i = 0; i < Exits.Length; i++)
{
description += " " + Exits[i].Name;
if (i != Exits.Length - 1)
description += ",";
}
description += ",";
return description;
}
}
}
Make location class public if it's not public already
You need to declare your class this way:
public abstract class location
{
...
}
As an aside, general code style has classes starting with a capital letter (ie. Location).
C# defaults the accessibility to internal, so having public methods inside the class will cause this error (like your constructor, properties, and virtual method). A good rule of thumb is to always declare classes public unless you know for sure you want everything in them to be internal or lower.
See MSDN for more information on access modifiers.
Related
I am learning C# and made a simple "Player" class. But I struggle having multiple overload.
Here's my best solution but I feel like it could be done simpler/better.
class Player : Entity
{
public Player() {
Name = "Player";
XP = 0;
LVL = 1;
XPToLvlUp = 10;
XpRank = 10;
}
public Player(string name) : this() {
Name = name;
}
public Player(string name, int _Hp, int _Mp) : this(name) {
HP = _Hp;
MP = _Mp;
}
public Player(string name, int _Hp, int _Mp, int _Xp, int _Lvl) : this(name, _Hp, _Mp) {
XP = _Xp;
LVL = _Lvl;
}
public Player(string name, int _Hp, int _Mp, int _Xp, int _Lvl, int XpByRank) : this(name, _Hp, _Mp, _Xp, _Lvl) {
XpRank = XpByRank;
}
//deleted code for better reading
private int XPToLvlUp;
private int XpRank;
public int XP;
public int LVL;
public string Name;
}
Is it good and if not please tell me why.
Thanks for your responses!
I think it's fine as is. One question to ask yourself: Are each of those methods actually likely to be called?
One option is to just let the programmer set those values after they've instantiated the class:
var myPlayer = new Player();
myPlayer.XP = 5;
However, there are situations where you really want all the info up front, so that may not be suitable.
Another option could be an options class that is passed to the ctor:
public class PlayerSettings
{
public Name = "Player";
public XP = 0;
public LVL = 1;
public XPToLvlUp = 10;
public XpRank = 10;
}
Then your ctors looks like this:
public Player() : this(new PlayerSettings())
{
}
public Player(PlayerSettings settings)
{
//Fill in appropriate variables here
}
That option would be called in this way:
var playerSettings = new PlayerSettings() { XP = 5 };
var myPlayer = new Player(playerSettings());
In the end, I'm not sure one is "better" than the other, it largely depends on your needs.
Your class is almost good and acceptable.
Short story: use Properties.
Long story:
First of all make or follow the naming rules, it will make your code more friendly to read. It's up to you, just a suggestion. For complex names consisting of multiple words you may use CamelCasedNames. And avoid shorten names for all types of data where it maybe useful. For example you may expand Lvl to Level but Xp to Experience will look as something odd. It's up to you too.
string name; // local Variable, first character lower cased
private string _name; // private Field, first character is lower cased with leading "_"
public string Name { get; set; } // public Property, first character is upper cased
I'll show you alternatives to overriden constructors and will follow the naming rules.
1) Default values for constructor (with a part of your class to keep it simple)
class Player
{
public Player(string name = "Player", int xp = 0, int level = 1)
{
Name = name;
Xp = xp;
Level = level;
}
// Properties instead of Fields
public int Xp { get; private set; } // restrict modification of the property outside of a class but reading is available
public int Level { get; private set; }
public string Name { get; set; }
}
2) Properties without constructor with default values
First Property purpose is restrict access to data to keep internal object data consistent. Even you make mistakes in the code. Good way to avoid some bugs.
Second property purpose is executing code while you're getting or setting one. For example, making properties dependent on each other to store less and only unique data.
class Player
{
public int Xp { get; private set; } = 0;
public int Level { get; private set; } = 1;
public string Name { get; set; } = "Player";
}
Usage
Player player = new Player() { Name = "KillerPWNZ", Level = 100, Xp = 999999 };
Bonus: Another Property feature
You can execute any code in get or set clause.
Let's assume that each next player's level require doubled amount of xp from previous but 2nd level requre 100 XP. And you decided to invoice to the 1st leveled player 1000 XP. Obviously you'll need to bump the Level few times. Assuming that Xp contains relative to Level value.
The invoice
player.Xp += 1000;
The Property with code
private int _xp = 0;
public int Level { get; private set; } = 1;
public int Xp
{
get => _xp; // same as: get { return _xp; }
set
{
_xp = value; // here value is keyword containing data you want to set
while (_xp >= GetXpPerLevel(Level))
{
_xp -= GetXpPerLevel(Level);
Level++;
}
while (_xp < 0 && Level > 1)
{
_xp += GetXpPerLevel(Level - 1);
Level--;
}
}
}
// helper method
private int GetXpPerLevel(int level)
{
if (level < 1) return 0;
// int result = 100;
// for (int i = 1; i < level; i++) result *= 2;
// return result;
// or the same with some binary shift magic :)
return 100 << (level - 1);
}
Suppose I have a list of Robot class [List< Robot> myList=new List< Robot>()]. Each Robot has a name and id depending on its colour. Now randomly pick values from the list and give an output of how many Robots of each colour are there on your list.
(N.B. Consider you have only 3 colored Robot[Yellow,green, red])
my code:
public class Test : MonoBehaviour
{
private void Start()
{
List<Robot> myList = new List<Robot>();
List<string> robotList = new List<string>();
robotList.Add("yellow");
robotList.Add("green");
robotList.Add("red");
int someNum = Random.Range(0, robotList.Count);
string robotNumber = robotList[someNum];
robotList.RemoveAt(someNum);
Robot robot;
int id = 0;
robot = new Robot(robotNumber, id);
Debug.Log(robot);
id++;
}
}
public class Robot
{
public string name;
public int id;
public Robot(string name, int id)
{
this.name = name;
this.id = id;
}
}
but this not work maybe.. actually I don't understand what actually my output is...
Not sure to really understand what you're asking for: if it's only about the meaning of the Debug.Log(robot); output, check for #Smartis answer as it answers it perfectly :)
Otherwise, I feel like you wanted to populate a List<Robot> with random picked names. In this case you need to use a loop: Start() method is only called once on start (as its name suggest). If you need to populate a list with random picked colors/names and then display how many of each colors/names are in the list you can do it as follow:
public class Test : MonoBehaviour
{
private void Start()
{
List<Robot> robotsList = new List<Robot>();
List<string> namesList = new List<string>();
namesList.Add("yellow");
namesList.Add("green");
namesList.Add("red");
PopulateRobotsList();
DisplayRobotsListContent();
}
private void PopulateRobotsList()
{
for(int id = 0; id < 100; id++)
{
string robotName = namesList[Random.Range(0, namesList.Count)];
robotsList.Add(new Robot(robotName, id));
//Debug.Log(robotsList[robotsList.Count - 1]);
}
}
private void DisplayRobotsListContent()
{
int[] robotsNamesCount = new int[namesList.Count];
for (int i = 0; i < robotsList.Count; i++)
{
robotsNamesCount[namesList.IndexOf(robotsList[i].name)] += 1;
}
for (int i = 0; i < namesList.Count; i++)
{
Debug.Log("Robot(s) named \"" + namesList[i] + "\" : " + robotsNamesCount[i]);
}
}
}
public class Robot
{
public string name;
public int id;
public Robot(string name, int id)
{
this.name = name;
this.id = id;
}
}
Please note I changed some variable names as I found it really hard to understand with the one you provided (ex: robotsList to store the potential colors/names of the robots is a weird choice of name :) ).
Hope this helps,
Your random pick works fine (even when your code is a little bit confusing). I guess your problem is, you don't understand the output of Debug.Log(robot);.
actually I don't understand what actually my output is... - OP
What does Debug.Log() print?
According to the Unity3D Documentation for this function will converted the given object to be to string representation for display. This means simply the return value of ToString() method on your Object will printed.
So let's have look at the Object.ToString() method and it's behavior in the MSDN Documentation.
Default implementations of the Object.ToString method return the fully qualified name of the object's type. - MSDN
So, your output in the Unity Log will be the Type Definition of your object.
Now, how to get useful information?
Just override the default ToString() method of your Robot class to something like this:
public class Robot
{
public string name;
public int id;
public Robot(string name, int id)
{
this.name = name;
this.id = id;
}
// Here start's the magic!
public override string ToString()
{
return string.Format("Robot -> Id:'{0}' Name:'{1}'", id, name);
}
}
I have to implement switchboard class which can have devices like Fan, AC, Bulb etc. My switch board class looks like below.
Which one is more object oriented?
1.
class SwitchBoard
{
public static int totalDevices=0;
public List<ElectricDevice> Devices { get; set; }
public SwitchBoard(int noOfFans, int noOfACs, int noOfBulbs)
{
Devices = new List<ElectricDevice>();
//int deviceCount = 0;
for (int i = 0; i < noOfACs + noOfBulbs + noOfFans; i++)
{
if (i < noOfFans)
{
Devices.Add(new Fan("Fan " + (i + 1),totalDevices));
}
else if (i >= noOfFans && i < noOfFans + noOfACs)
{
Devices.Add(new AC("AC " + (i - noOfFans + 1), totalDevices));
}
else
{
Devices.Add(new Bulb("Bulb " + (i - noOfFans - noOfACs + 1), totalDevices));
}
totalDevices++;
}
}
}
2.
class SwitchBoard
{
public static int totalDevices=0;
public List<ElectricDevice> Devices { get; set; }
public SwitchBoard(int noOfFans, int noOfACs, int noOfBulbs)
{
Devices = new List<ElectricDevice>();
CreateDevice(Devices, "Fan", noOfFans);
CreateDevice(Devices, "AC", noOfACs);
CreateDevice(Devices, "Bulb", noOfBulbs);
}
I am feeling like first one is best approach because in second method we are using method which intstantiate the class outside class by taking property outside the class and intializing outside it. Like taking out switch out of the swtichboard and connecting it to fan and placing it back into switch board.
I think it has something to do with encapsulation.
Pseudo code for CreateDevice
function CreateDevice(List<EelectricDevice> Devices, string type, int noOfObjects )
{
for(int i=Devices.Length; i<noOfDevices+Devices.Length;i++)
{
if(type=="Fan")
Device[i]=new Fan("Fan"+i);
else if(type=="AC")
Device[i]=new AC("AC"+i);
else if(type=="Bulb")
Device[i]=new Bulb("Bulb"+i);
}
}
I would refactor that loop into a generic method to which you send a number (which represent the number of devices, and pass it function which returns an Electrical device:
public delegate ElectricDevice Func<ElectricDevice>()
private addElectricalDevices(int number, Func<int, ElectricDevice> createDeviceFunc)
{
for(int i = 0; i < number; i++)
{
Devices.Add(createDeviceFunc(i));
}
}
Example usage:
addElectricalDevices(10, (i) => new Fan("Fan " + (i + 1)));
Sorry, it's been long since i wrote C# code so this might not be perfect.
Designing of a system is something that is difficult to suggest as there are many things that needs to be considered.
But keeping it minimum and sticking to your question, there are a few things that needs to be considered -
Suggest to have an interface like ISwitchable, to be implemented by ElectricDevice which are switchable. This is important conceptually, as not all ElectricDevices are meant to be switchable -
public interface ISwitchable
{
}
Now you can implement this in you device something like this -
public class Fan : ElectricDevice, ISwitchable
{
//Implementation of fan
}
For the total number of devices, it should be implemented as a read only property, which is calculated as a sum of all the devices, other there is a possibility of this value get tampered. There is no need to calculate this value with each iteration.
At the end this is how you SwitchBoard class might look like -
public class SwitchBoard
{
public List<ISwitchable> switchableDevices { get; private set; }
public int TotalSwichableDevices
{
get { return numberOfFans + numberOfACs + numberOfBulbs; }
}
private readonly int numberOfFans;
private readonly int numberOfACs;
private readonly int numberOfBulbs;
public SwitchBoard(int noOfFans, int noOfACs, int noOfBulbs)
{
this.numberOfFans = noOfFans;
this.numberOfACs = noOfACs;
this.numberOfBulbs = noOfBulbs;
switchableDevices = new List<ISwitchable>();
switchableDevices.AddRange(this.AddFans());
//TODO: Add other devices
}
private IEnumerable<ISwitchable> AddFans()
{
List<ISwitchable> fans = new List<ISwitchable>();
for (int i = 0; i < numberOfFans; i++)
{
fans.Add(new Fan());
}
return fans;
}
}
Working on a program for class, and am about 95% complete, but have run into a roadblock. I've got a Flight class that holds information about the flight, as well as a seating chart. Using a windows form listbox to select from the flight objects I created by reading from a text file. I can get values for every property from the class object, except for one, SeatChart.
Here's the pertinent code in the main program:
private void lstFlights_SelectedIndexChanged(object sender, EventArgs e)
{
curFlight = (Flight)lstFlights.SelectedItem;
DisplayNewFlightChart();
}
private void DisplayNewFlightChart()
{
int seats = curFlight.Rows * curFlight.Seats;
lstSeatingChart.Items.Clear();
string[] seatChart = curFlight.SeatChart;
for (int x = 0; x <= seats; x++)
{
lstSeatingChart.Items.Add("Seat " + (x + 1) + " " + seatChart[x]);
}
}
And here is the code from the class:
class Flight
{
private string mPlane;
private string mDepartureTime;
private string mDestination;
private int mRows;
private int mSeats;
private string[] mSeatChart;
public Flight()
{
}
// Create the overloaded Constructor
public Flight(string planeType, string departureTime,
string destination, int numRows,
int numSeatsPerRow)
{
this.Plane = planeType;
this.DepartureTime = departureTime;
this.Destination = destination;
this.Rows = numRows;
this.Seats = numSeatsPerRow;
this.SeatChart = mSeatChart;
mSeatChart = new string[Rows * Seats];
for (int seat = 0; seat <= mSeatChart.GetUpperBound(0); seat++)
{
mSeatChart[seat] = "Open";
}
}
public string Plane
{
get { return mPlane; }
set { mPlane = value; }
}
public string DepartureTime
{
get { return mDepartureTime; }
set { mDepartureTime = value; }
}
public string Destination
{
get { return mDestination; }
set { mDestination = value; }
}
public int Rows
{
get { return mRows; }
set { mRows = value; }
}
public int Seats
{
get { return mSeats; }
set { mSeats = value; }
}
public string[] SeatChart
{
get { return mSeatChart; }
set { mSeatChart = value; }
}
public void MakeReservation(string passName, int seat)
{
bool seatTaken = false;
if (mSeatChart[seat] != "Open") seatTaken = true;
if (passName != "" && seatTaken == false)
{
mSeatChart[seat] = passName;
}
else
{
MessageBox.Show("Please Enter a Passenger Name, in an unreserved seat");
}
}
It's telling me the curFlight.SeatChart is null, even though I can pull .Rows and .Seats from the curFlight just fine. I have no clue why .SeatChart is messing up. lstFlights is the list of flight objects pulled from the text file, and lstSeatingChart is where I want to display a list of seats.
You are setting SeatChart to mSeatChart, which is null at that time. So no reference to an object is made for this.SeatChart.
After that you initialize mSeatChart and fill it.
You should move setting this.SeatChart after initializing mSeatChart.
mSeatChart = new string[Rows * Seats];
this.SeatChart = mSeatChart;
Edit:
In addition, SeatChart is the property and mSeatChart is the member variable. SeatChart will be used to expose mSeatChart, so it's really weird to set SeatChart with mSeatChart. So weird that I didn't even think you were doing that.
So in your case leave the following out in the constructor:
this.SeatChart = mSeatChart;
I think the actual cause of your issue is somewhere else in the code, where you initiate Flight and fill the list. If I understand correctly you get a null reference error on the concatenation in the for loop?
string[] seatChart = curFlight.SeatChart;
for (int x = 0; x <= seats; x++)
{
lstSeatingChart.Items.Add("Seat " + (x + 1) + " " + seatChart[x]);
}
Check where you initate each Flight object. I bet you are using the empty constructor: new Flight()
Remove the empty constructor, because you don't expect empty values apparently. And if you really need the empty constructor then either initiate all member variables as expected or perform a null check wherever you want to use them.
And once you found the cause, make sure you change the for loop to
for (int x = 0; x < seats; x++)
since you are checking for the number of seats and do a zero-based loop. If x = seats you would have performed the loop seats + 1 times (rows*seats + 1).
If your code relies on a particular property never being null, you need to make sure it is initialized in all constructors.
Based on the logic of your class, I would suggest you shouldn't have a parameter less constructor. It doesn't make sense to have a flight that didn't have a known number of seats (in your implementation at least).
Also some style things.
You don't need to declare your private instance variables. Just use
public string destination {get; set;}
Declare "open" as a class constant and use that constant rather than the hard coded string value.
I am trying to learn C# and I am up to an example that uses a boolean. For the life of me I cant figure out why the program isnt noticing that I am trying to pass a value of true to the boolean. Here is the code in the Form.cs:
namespace WindowsFormsApplication7
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
HappyBirthday birthdayMessage = new HappyBirthday();
string returnedMessage;
birthdayMessage.PresentCount = 5;
birthdayMessage.MyProperty = "Adam";
birthdayMessage.hasParty = true;
returnedMessage = birthdayMessage.MyProperty;
MessageBox.Show(returnedMessage);
}
}
}
Here is the Class that I created:
class HappyBirthday
{
//====================
// CLASS VARIABLES
//====================
private int numberOfPresents;
private string birthdayMessage;
private bool birthdayParty;
//===========================
// DEFAULT CONSTRUCTOR
//===========================
public HappyBirthday()
{
numberOfPresents = 0;
//birthdayParty = false;
}
//===========================
// METHOD
//===========================
private string getMessage(string givenName)
{
string theMessage;
theMessage = "Happy Birthday " + givenName + "\n";
theMessage += "Number of presents = ";
theMessage += numberOfPresents.ToString() + "\n";
if (birthdayParty == true)
{
theMessage += "Hope you enjoy the party!";
}
else
{
theMessage += "No party = sorry!";
}
return theMessage;
}
//================================
// READ AND WRITE PROPERTY
//================================
public string MyProperty
{
get { return birthdayMessage; }
set { birthdayMessage = getMessage(value); }
}
//================================
// WRITE-ONLY PROPERTY
//================================
public int PresentCount
{
set { numberOfPresents = value; }
}
public bool hasParty
{
set { birthdayParty = value; }
}
}
Now I set the initial value to false (even though if my understanding is correct that should be the default value), but when I try to set it = true, the program does not recognize it. Am I supposed to pass a boolean differently then I would a string or int?
You're setting MyProperty before you're setting hasParty. getMessage() is not being called every time MyProperty is polled.
The way MyProperty works is confusing, because the set and get deal with different values (you set the name, and then get the whole message, which is confusing). I'd replace it with a GivenName property and then make the GetMessage() (or expose it as a read-only property Message) public.
Also, you can make your code much simpler by using auto-properties (you can use private gets to keep the write-only behavior, though in the real world write-only properties are very rare, and you should probably just make them public like the sets). And since the default int value is 0, you don't need to specify your default constructor. Here's how the code looks now:
class HappyBirthday
{
public string Message
{
get
{
string theMessage;
theMessage = "Happy Birthday " + GivenName + "\n";
theMessage += "Number of presents = ";
theMessage += PresentCount.ToString() + "\n";
if (HasParty)
{
theMessage += "Hope you enjoy the party!";
}
else
{
theMessage += "No party = sorry!";
}
return theMessage;
}
}
public string GivenName { private get; set; }
public int PresentCount { private get; set; }
public bool HasParty { private get; set; }
}