How to get field of new instance from one class to another - c#

So I am implementing a rudimentary Hotel booking system with a check in and check out method. For the check in and check out selections, I have a switch statement that allows the user to pick a room by selecting a number. I have a truncated version of the code below, as the case statement pretty much are similar in nature:
case 1:
if (true)//this is supposed to check if the room is booked
{
Console.WriteLine("This room is already booked, please try another place");
}
else
{
var instance1 = new BookingMethods();
instance1.bookRoom1();
revenueGenerated += 100;
}
break;
For the if statements, I'm trying to check if the rooms are booked or not, and I have created objects in another class along the methods for checking in and out. Below is how I did this:
class BookingMethods
{
Room room1 = new Room();
public void bookRoom1()
{
//code for generating ints
room1.occupied = true;
room1.numGuests = guests1;
room1.daysBooked = staying1;
room1.roomType = "luxury";
}
}
I want the if statement to be able to read room1.occupied for the condition, but I can't seem to be able to reference it correctly. I have bookRoom1() in my BookingMethods.cs from the case statement, which is in my Program.cs. I figured I don't have to move my Room object from BookingMethods.cs to Program.cs to do this, and I haven't been able to figure out a way to call it.

You did not specify an access modifier for room1. If you do not provide one, it is set to private by default, meaning it is only accessible in the class in which it is declared. In addition to this, you should really create a property.
This should get you going:
var instance1 = new BookingMethods();
switch (instance1.room1.occupied)
{
case true:
Console.WriteLine("This room is already booked, please try another place");
break;
default:
instance1.bookRoom1();
revenueGenerated += 100;
break;
}
class BookingMethods
{
public Room room1 {get; set;};
public void bookRoom1()
{
//code for generating ints
room1.occupied = true;
room1.numGuests = guests1;
room1.daysBooked = staying1;
room1.roomType = "luxury";
}
}

Make room1 a field
class BookingMethods
{
Room room1 {get; set;};
public void bookRoom1()
{
//code for generating ints
room1.occupied = true;
room1.numGuests = guests1;
room1.daysBooked = staying1;
room1.roomType = "luxury";
}
}

Related

List resetting when adding new items (Edited title to reflect actual issue)

Original Title: Get-Set to add Object w/multiple properties into a list C#
Edit: I had originally thought the issue was in setting up properties for the list objects, when it was an issue with regards to where I had initialized the list in my main code class.
Original Post:
New to coding, taking a C# course. We're working on encapsulation and get:set/properties.
The assignment says that we have to build a class that creates a die with an input number of sides, and "roll" the die for a random number. Easy!
In a second class, we have to build a function to add or remove any number of dice to the pool, and then roll them all for a result.
I'm assuming they want the dice pool to be a list that is private.
My logic going in was to create the single OneDie class, and then using a xDy notation in the main program prompt to add x number of die with y sides to the list. (ie: add 2d6)
I've built an AddDie function that should do that, but when I check my list count after it's done, the count is 0. The private list (_dicePool) seems to be re-setting to zero every time I try to add a new object to the list. I suspect I'm not building my property DicePool's get/set functionality correctly, but I'm not sure how to call my 2-parameter AddDice function from inside the DicePool{set}, or even if that's the approach I should take.
Assuming the list should be private, am I missing something to permanently add new objects to the list?
Edit to add: OR, would it be better to create a ManyDice object? But how do I build this.Sides and this.Roll from the OneDie object?
Here's my code that's applicable to adding objects (dice) to the list (dicepool).
class ManyDice
{
private List<OneDie> _dicePool = new List<OneDie>();
//What I think I might have to do:
public List<OneDie> DicePool
{
get
{
return this._dicePool;
}
set
{
//???????????? how do I call AddDice, when I need 2 parameters for it?
}
}
public void AddDie(int number, int sides)
{
for (int i = 0; i < number; i++)
{
this.dicePool.Add(new OneDie(sides));
}
}
}
class OneDie
{
private int _sides, _rolledValue;
public int Sides
{
get
{
return this._sides;
}
set
{
this._sides = value;
}
}
public int RollValue
{
get
{
return this._rolledValue;
}
set
{
this._rolledValue = value;
RollIt(value);
}
}
public OneDie()
{
}
public OneDie(int sides)
{
this.Sides = sides;
this.RollValue = sides;
}
private int RollIt (int sides)
{
Random random = new Random((int)DateTime.Now.Ticks);
return random.Next(1, (sides + 1));
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Let's roll some dice!");
Console.WriteLine("Please enter the number of dice you want to roll in the following format:");
Console.WriteLine("xdy, where \"x\" is the number of dice you want and \"y\" is how many sides they have.");
Console.WriteLine("Example: 2d6 is 2 6-sided dice. Perfect for playing Catan! (or Monopoly)");
Console.WriteLine("Please limit your dice to d4, d6, d8, d10, d12, d20");
Console.WriteLine("To add a die, type \"add xdy\" to add x number of y sided dice.");
Console.WriteLine("To remove a die, type \"remove xdy.\" to remove x number of y sided dice.");
Console.WriteLine("Type \"dice\" to see a list of all the dice in the pile.");
Console.WriteLine("Type \"roll\" to roll all the dice and see the results!");
Console.WriteLine("Type \"clear\" to clear all the dice and start agin!");
Console.WriteLine("Type \"exit\" to exit program\n");
PlayDice();
Console.ReadKey();
}
static void PlayDice()
{
do
{
string[] xDy = null;
int numberOfDice = 1;
int numberOfSides=1;
Console.WriteLine("\nEnter your command:");
string option = Console.ReadLine();
option = option.ToLower().Trim();
string[] words = option.Split(' ');
string command = words[0];
//CheckCommand(command);
if (words.Length > 1)
{
xDy = words[1].Split('d');
numberOfDice = int.Parse(xDy[0]);
numberOfSides = int.Parse(xDy[1]);
}
ManyDice die = new ManyDice();
if (command == "exit")
{
Console.WriteLine("Thank you, play again, soon!");
break;
}
else if (command == "add")
{
//numberOfSides=CheckDice(numberOfSides);
die.AddDie(numberOfDice, numberOfSides);
Console.WriteLine("You have {0}ed {1} {2}-sided dice.", command, numberOfDice, numberOfSides);
}
else if (command == "remove")
{
Console.WriteLine("You have {0}d {1} {2}-sided dice.", command, numberOfDice, numberOfSides);
}
else if (command == "dice")
{
Console.WriteLine("These are your dice:");
die.Display();
}
else if (command == "roll")
{
Console.WriteLine("Here is your roll:");
}
else if (command == "clear")
{
Console.WriteLine("All dice have been cleared.");
}
} while (true);
}
static int CheckDice(int sides)
{
List<int> check = new List<int> {4,6,8,10,12,20};
while (!check.Contains(sides))
{
Console.WriteLine("{0}-sided dice are not available.\nPlease enter 4,6,8,10,12 or 20");
sides = int.Parse(Console.ReadLine());
}
return sides;
}
static string CheckCommand(string instructions)
{
List<string> check = new List<string> { "add", "remove", "dice", "roll","clear", "exit" };
while (!check.Contains(instructions))
{
Console.WriteLine("Command not recognized.\nPlease enter \"add\", \"remove\", \"dice\", \"roll\",\"clear\", or \"exit\"");
instructions = Console.ReadLine();
}
return instructions;
}
}
New Answer based on comments and updated question:
The line ManyDice die = new ManyDice(); is wiping your dice list clean every loop through your program. It's replacing your variable with a new instance of the class, with a fresh list and all.
Simply move that line before the start of the loop:
before the line do {
and then every iteration will use the same instance of ManyDice, and will all share the variable die, without overwriting it.
OLD ANSWER: From what I can see, your program only runs once. And then you need to start it again to put in another dice. Your main function only asks for input once. Whenever you start the program again, all the memory used in the program gets cleared. Unless I’m missing something, that is why your list continues to be reset. You’re actually running a completely new program the next time you try to add dice. So it has no knowledge of the previous runs.
One solution is to say (pseudo code)
While (running) {
// what you have now
if (option == “done”) running = false;
if (option == “roll”) // roll all dice.
}
This will keep prompting the user for commands until they type done. And it remains the same program so that you don’t lose the data from earlier commands.
Update based on comment: you’re recreating the ManyDice instance on each iteration, effectively starting from scratch. Save the instance outside the while loop and then reuse it.
Hint:
Your rolling should probably be done by manyDice.RollAll() And maybe should return a List of RollResults.

Save/load value&color to txt/xml

I'm completely new to C# programming. (4 days)
I did some amateur programming on Excel VB to create an dashboard for our teams. Now I want to lift it to the next level.
My dashboard has 10 buttons changing color from red to green and back each click. Each click changes the label beneath it from Text 1 to Text 2.
Function: overview of the service area who finished work and handed to us.
I got 10 different voids for each of the areas, because I didn't manage to link the clicked button to the label, so I just used 10. "northwest", "east", "southeast" and so on.
void northwest(object sender, EventArgs e)
{
Button button = sender as Button;
Control ctrl = ((Control)sender);
switch (ctrl.BackColor.Name)
{
case "Red":
ctrl.BackColor = Color.LimeGreen;
ctrl.ForeColor = Color.Black;
lbl_nw.Text = "Service";
break;
case "LimeGreen":
ctrl.BackColor = Color.Red;
ctrl.ForeColor = Color.White;
lbl_nw.Text = "North-West";
break;
default:
ctrl.BackColor = Color.LimeGreen;
ctrl.ForeColor = Color.Black;
lbl_nw.Text = "Service";
break;
}
}
Second: I got 145 buttons (the teams) changing between 5 different colors each click. We use this to clarify the state of the team. like "n/a", "available", "on the way" etc.
void MyButtonClick(object sender, EventArgs e)
{
Button button = sender as Button;
Control ctrl = ((Control)sender);
switch (ctrl.BackColor.Name)
{
case "Gainsboro": // switch from n/a to available
ctrl.BackColor = Color.LimeGreen;
ctrl.ForeColor = Color.White;
break;
case "LimeGreen": // switch from available to on the way
ctrl.BackColor = Color.Yellow;
ctrl.ForeColor = Color.Black;
break;
case "Yellow": // switch from on the way to fully occupied
ctrl.BackColor = Color.Orange;
ctrl.ForeColor = Color.Black;
break;
case "Orange": // switch from fully occupied to omitted
ctrl.BackColor = Color.Red;
ctrl.ForeColor = Color.White;
break;
case "Red": // switch from omitted to back at home
ctrl.BackColor = Color.Aqua;
ctrl.ForeColor = Color.Black;
break;
default:
ctrl.BackColor = Color.Gainsboro;
ctrl.ForeColor = Color.Black;
break;
}
}
Now the actual problem: Because my former dashboard can only be used by one person (on one central computer) I wanted to make my new dashboard multi-user friendly.
So my intention is to save the colors/label names into an txt file or xml file (forget about xml. its toooo complex for me) and load it automatically if there were any changes on the board by any user.
I searched but can't find any solution I really understand (because of missing knowledge).
I have tried the following:
void save(object sender, EventArgs e)
{
TextWriter txt = new StreamWriter("C:\\Users\\shift.txt");
txt.Write(button1.Text);
txt.Write(button1.ForeColor);
txt.Write(button1.BackColor);
txt.Close();
}
At this solution I would need to write every button I want to store. Problem: I get something like this:
North-West Color [LimeGreen] Color [Black].
I think the correct solution would be like North-West;LimeGreen;Black, but I cant figure out how! Even then I don't know how to load back all values to the correct objects.
And I tried to loop through them with:
for (int i = 1; i < 155; i++)
But I got problems with the objects (e.g. "is a type but used as an method") every combination.
Second attempt was via xml. But this is way to complex for me.
I tried it like this:
void savedata(object sender, EventArgs e)
{
DataSet d = new DataSet();
DataTable t = new DataTable();
d.Tables.Add(t);
t.Columns.Add(new DataColumn("Name",typeof(string)));
t.Columns.Add(new DataColumn("FontColor",typeof(string)));
t.Columns.Add(new DataColumn("Background",typeof(string)));
t.Rows.Add(button6.Text, button6.ForeColor, button6.BackColor);
t.Rows.Add(button7.Text, button7.ForeColor, button7.BackColor);
t.Rows.Add(button8.Text, button8.ForeColor, button8.BackColor);
t.Rows.Add(button9.Text, button9.ForeColor, button9.BackColor);
t.Rows.Add(button10.Text, button10.ForeColor, button10.BackColor);
t.Rows.Add(button11.Text, button11.ForeColor, button11.BackColor);
t.Rows.Add(button12.Text, button12.ForeColor, button12.BackColor);
t.Rows.Add(button13.Text, button13.ForeColor, button13.BackColor);
t.Rows.Add(button14.Text, button14.ForeColor, button14.BackColor);
t.Rows.Add(button15.Text, button15.ForeColor, button15.BackColor);
d.WriteXml("C:\\Users\\Shift.xml");
}
It saves, yes, but how do I load it back into the correct objects?
I don't understand anything about de/serialization, I just read some code and tried to adopt it and failed big.
My question:
Does anyone have a proper solution for the text file how to save/load/format correctly? The save should be done if there is a new change, same goes for the load. So automatically. Every user can open the dashboard, the file storage would be on our server where everyone has access to it to save/load it via dashboard.
I would really appreciate a possible solution for this. Like I said, I'm a beginner and this problem driving me nuts.
Everything works fine, except for the save/load mechanic.
Just to mention:
i cant download any other software, so i need to stick to windows programms.
for saving and loading from a flat file (.txt, .xml, .json, .whatver) I use the following solution, it's honestly very easy and takes the hard work away. You will need to download and install NewtonSoft package from nuget. But trust me it makes everything easy.
Let's create a hypothetical scenario similar to yours. You have many users and want to save information for each of them. Let's start with a user class.
public class User {
public int Id { get; set; }
public string Name { get; set; }
public string Theme { get; set; }
}
Now lets create a little 'database' or 'data holder' class that will actually store and lookup our info. It will have a FilePath variable where we will save our flat file, a List of all the Users and some functions to help get everything:
public static class DataHolder {
public static string FilePath { get; set; }
public static List<Users> Users { get; set; }
static DataHolder()
{
// Initialize the variables
FilePath = "C:/Temp/MyData.json";
Users = new List<User>();
Init();
}
static void Init() { }
public static void LoadData() { }
public static void SaveData() { }
}
Let's start by creating a file to save all the data in.
using System.IO;
using NewtonSoft;
...
static void Init() {
// Check if file exists
if (File.Exist(FilePath))
{
LoadData();
return;
}
// It doesn't so we create it
using (var sw = new StreamWriter(FilePath)){
// Create empty list of users
var emptyUserList = new List<User>();
// Convert it into Json using NewtonSoft
var fileText = JsonConvert.SerializeObject(emptyUserList);
// Write it to the file
sw.Write(fileText);
}
}
Above we used Newton to create a new file with an empty Json object in it. Let's see how loading and saving will work:
public static void LoadData() {
using (var sr = new StreamReader(FilePath)){
Users = JsonConvert.Deserialize<List<User>>(sr.ReadToEnd());
}
}
public static void SaveData() {
using (var sw = new StreamReader(FilePath)){
sw.Write(JsonConvert.Serialize(Users));
}
}
And that is the main bulk of the class. Let's implement a GetUser() and AddUser() function that searches for a user based on Id and then I'll show you an example of how to use the class:
using System.Linq;
...
public static User GetUser(int id) => Users.FirstOrDefault(x => x.Id = id);
public static void AddUser(User user) => Users.Add(user);
Example:
public static void Main(string[] args){
// Test the saving
TestSaving();
// Test the loading
TestLoading();
}
static void TestLoading() {
// Get a user
var loadedUser = DataHolder.GetUser(1);
if (loadedUser != null)
Console.WriteLine("Success");
}
static void TestSaving() {
// Create a new user and add it
var user = new User() {
Id = 1,
Name = "Fred",
Theme = "Red"
};
// Save the data in memory
DataHolder.AddUser(user);
// Save the data to file
DataHolder.SaveData();
}

(C#) Instantiated Under Conditions, Else Reuse

I'm extremely new to programming so bear with me.
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
GameObject newItem = Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
newItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
newItem.GetComponent<ButtonSetter>().item_count += 1;
newItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
//SEE CONTINUED POST
}
}
So, within the else statement, I want to basically say,
They got their first wood, so we created a panel to display information about the wood they have. Now I want to say, since we have already instantiated the displayPanel, we just want to work from that and adjust the object's variable that controls the integer part of "Wood: 0"
This should be
newItem.GetComponent<ButtonSetter>().item_count
But if I try accessing that within the else statement as shown:
newItem.GetComponent<ButtonSetter>().item_count += 1;
It tells me that newItem does not exist within the current context.
How can I Instantiate something under certain conditions, then after the condition was met, take the instantiated object and work with its script/variables?
Visual representation of my issue
I think you need to restructure your code a little bit.
So here's how I would do it
public class Item : MonoBehaviour {
public string itemName;
public int count;
}
I will create a base class Item for wood, iron or whatever is there in your game.
Next, I will Create a Handler Script, that keeps track of all Items that are clicked/not Clicked
Handler.cs
public class Handler : MonoBehaviour {
public Item itemPrefab;
public Dictionary<string, Item> displayItems= new Dictionary<string, Item>();
public void OnWoodClicked()
{
OnItemClicked("wood");
}
public void OnIronClicked()
{
OnItemClicked("iron");
}
private void OnItemClicked(string itemKey)
{
if (displayItems.ContainsKey(itemKey))
{
displayItems[itemKey].count++;
}
else
{
Item item = Instantiate(itemPrefab);
item.count = 1;
item.itemName=itemKey;
displayItems.Add(itemKey, item);
}
}
}
So To keep track of created items, I have created a dictionary public Dictionary<string, Item> displayItems;
In this script OnItemClicked(string itemKey) Method will check If Item of this type is already created or not. (By checking if that key exists)
If that item is not created then we will Instantiate new Item (Prefab of display Item you want to show) and add that to the dictionary according to its key.
But if it already exists, then simply access that object as displayItems[itemKey] as you like
So for example, if you click on Wood, you will get access to wood display item as displayItems["wood"].
I hope this helps.
Just place a comment if you want me to make it clearer.
As it's currently written, newItem only lives in the "IF" clause of the code, so the "ELSE" clause can't see it.
If you assign it to a class-level field (like it appears you have _hasWood assigned at the class level) then you will be able to access it in either the "IF" or "ELSE" blocks, and maintain the count for the life of the object "clickWood()" is called from.
private bool _hasWood;
private GameObject _woodItem;
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
_woodItem= Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
_woodItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
_woodItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
}
}

Accessing class from form and vice-versa

After hunting the internet for two days and not finding a solution that I can understand properly I have to ask on here for an answer.
I have a windows forms application that was written in vb.net and works fine. I have decided to rewrite this in c# which I thought wouldn't be too much of a problem but ...
I have two classes in the project :
FormJobs & AppJobs
FormJobs contains methods and functions that modify the forms in some way.
AppJobs contains methods and functions for everything else (Checks,Scanning and so forth).
On my main form (FrmStart) the On_load event uses a function from AppJobs to check that the network is up (public bool CheckNetConnection) and then Checks to make sure that the root save folder exists (public void CheckRoot).
If CheckNetConnection is false or CheckRoot does not exist then a method in the FormJobs class sets some buttons to disabled, some labels to display information as to what has gone wrong and also sets the height of the form.
The above works in VB.net but I keep getting a StackOverflowException or NullReferenceException with the C# code.
I know the reason for the Exceptions is because the two classes and the form all keep calling each other so I know that I need to remove this code but I am not sure how to let each class and the form access each other. It is obviously bad design as I`m just starting to learn C# so any help on this would be much appreciated.
But my main questions are:-How do I get a form to access multiple classes?
Allow the classes to access each other?
Let the classes make changes to a form?
FrmStart Code
AppJobs Appjobs = new AppJobs();
private void FrmStart_Load(object sender, EventArgs e)
{
KeyPreview = true;
if (Appjobs.CheckNetConnection(this) == true)
{
Appjobs.CheckRoot(this);
}
AppJobs Code
public class AppJobs
{
FormJobs Formjobs = new FormJobs();
public string AppRoot = Properties.Settings.Default.DefaultFolder;
public string DefaultDevice = Properties.Settings.Default.DefaultScanner;
public bool NoDirectory = false;
DialogResult MsgBoxQuestion;
public bool CheckNetConnection(Form StartForm)
{
IPHostEntry ServerIP = new IPHostEntry();
bool ConnectedToServer = false;
string CurrentRoot = "MyServer";
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
try
{
IPHostEntry DNSTest = Dns.GetHostEntry(CurrentRoot);
if (DNSTest.AddressList.Length > 0)
{
ConnectedToServer = true;
}
else
{
ConnectedToServer = false;
}
}
catch (System.Net.Sockets.SocketException ex)
{
ConnectedToServer = false;
}
}
return ConnectedToServer;
}
public void CheckRoot(Form StartForm)
{
if (string.IsNullOrEmpty(AppRoot))
{
Formjobs.SetHeight(StartForm);
return;
}else if(AppRoot == "0")
{
Formjobs.SetHeight(StartForm);
return;
}
else
{
if ((!Directory.Exists(AppRoot)))
{
NoDirectory = true;
MsgBoxQuestion = MessageBox.Show(AppRoot + " is set, but the directory does not exist." + Environment.NewLine
+ Environment.NewLine + "Would you like to create the folder now?", "Root folder missing", MessageBoxButtons.YesNo);
if (MsgBoxQuestion == DialogResult.Yes)
{
Directory.CreateDirectory(AppRoot);
NoDirectory = false;
}
else
{
MessageBox.Show("You will not be able to use this program until you create a root folder.", "No root folder selected",MessageBoxButtons.OK);
}
}
}
}
}
FormJobs Code
public class FormJobs
{
AppJobs Appjobs = new AppJobs();
public void SetHeight(Form StartForm)
{
if (Appjobs.AppRoot == null | Appjobs.AppRoot == "0") {
if (Appjobs.DefaultDevice == null | Appjobs.DefaultDevice == "0") {
if (StartForm.Controls["MenuStrip1"].Visible == true) {
StartForm.Height = 167;
StartForm.Controls["LblNoRoot"].Visible = true;
StartForm.Controls["LblNoRoot"].Location = new Point(0, 24);
StartForm.Controls["LblNoRoot"].Text = "There is no root folder selected. Please select a root folder to continue.";
StartForm.Controls["LblNoDevice"].Visible = true;
StartForm.Controls["LblNoDevice"].Location = new Point(0, 48);
StartForm.Controls["LblNoDevice"].Text = "There is no default device selected. Please select a default device to continue.";
StartForm.Controls["BtnOkTickets"].Enabled = false;
StartForm.Controls["BtnQueryTickets"].Enabled = false;
StartForm.Controls["BtnSearch"].Enabled = false;
}else
{
StartForm.Height = 147;
StartForm.Controls["LblNoRoot"].Visible = true;
StartForm.Controls["LblNoRoot"].Location = new Point(0, 9);
StartForm.Controls["LblNoRoot"].Text = "There is no root folder selected. Please select a root folder to continue.";
StartForm.Controls["LblNoDevice"].Visible = true;
StartForm.Controls["LblNoDevice"].Location = new Point(0, 33);
StartForm.Controls["LblNoDevice"].Text = "There is no default device selected. Please select a default device to continue.";
StartForm.Controls["BtnOkTickets"].Enabled = false;
StartForm.Controls["BtnQueryTickets"].Enabled = false;
StartForm.Controls["BtnSearch"].Enabled = false;
}
}
One of the causes of your problems is that everyone is changing your StartForm. Apart from that this spaghetti makes it difficult to understand, it certainly doesn't help to make your classes reusable and maintainable if your Startform changes.
It seems to me, that AppJobs is meant to decide what the form should look like (for instance it decides that the StartForm should change height), while FormJobs performs the calculations needed to change this height. StartForm apparently is just allowing to let everyone make changes to him.
A better design would be that StartForm would not ask AppJobs to change its size, and to ask the operator whether a folder should be generated. Instead if ought to ask appJobs for advise: "Which height do you think I should have". after that it could ask FormJobs: "Please adjust my height according to this specification"
FormJobs should trust StartForm that it has gathered the correct information about how a StartForm ought to look like. FormJobs should not ask AppJobs for any information: "Hey AppJobs, StartForm asked me to change its appearance to certain specifications, but I'm not certain whether StartForm has done its job correctly. Please tell me if these specifications are correct, and give me some missing information")
The correct division into tasks would be:
AppJobs specifies the format of any StartForm according to its internal state (a.o. AppRoot, and existence of certain folders)
StartForm is the one who displays all items. He decides who to ask for specifications, and what to do with the returned specifications. He is also the only one who communicates with operators
FormJobs is a class that apparently knows all elements from a StartForm. If you will only have one type of StartForm, then Appjobs should be part of the Startform class. If you think there might be several different Startform classes, all with the same elements that ought to be manipulated similarly, shouldn't all these StartForm classes be derived from a FormJobs class?
Anyway, redesign without everyone causing to manipulate StartForm
Apparently there are a limited number of StartForm layouts depending on AppRoot, defaultDevice etc. You seem to be missing some "else" after your if, so this list might not be accurate. Still you will get the idea:
enum StartFormLayouts
{
DefaultDevice0,
AppRoot0,
Other,
}
// class that specifies the layout of any startform
class AppJobs
{
private bool IsAppRoot0
{
get{return Appjobs.AppRoot == null || Appjobs.AppRoot == "0";}
}
private bool IsDefaultDevice0
{
get{return Appjobs.DefaultDevice == null || Appjobs.DefaultDevice == "0";}
}
public StartFormLayoug GetPreferredLayout()
{
if (this.IsAppRoot0)
{
if (this.IsDefaultDevice)
{
return StartFormLayout.DefaultDevice0;
}
else
return StartFormLayout.AppRoot0;
}
else
{
return StartFormLayout.Other;
}
}
public bool ShouldAskDirectoryCreation()
{
return (!this.IsAppRoot0 && !Directory.Exists(AppRoot));
}
}
Note that this class doesn't need StartForm, nor AppJobs. It could work with any class that wants to know if it should ask for DirectoryCreation. Since it also does not speak any language, even a Chinese StartForm could use it. After all, the StartForm is the only one who knows what language it speaks and what to do if a certain layout is requested.
Furthermore, did you notice that I used a double || to use a Boolean OR instead of a bitwise or?
And I use statements like if (a) instead of if(a=true) a C# Boolean is a real Boolean, in contradiction to Booleans in C and C++.
The class of all kinds of forms that should be able to be layout according to the requested layout contains the functions similar to your
It depends a bit of whether you decide to let it be a base class of StartForm or StartForm itself. If you want it to handle every form class that has the required controls, consider of using an interface:
public Interface IStartForm
{
public int Height {get; set;}
public Label LabelNoRoot {get;}
public Label LabelNoDevice {get; }
public Button BtnTickets {get;}
...
This way you can set the size of any form that has these labels and buttons, even if they have different names than those strings you use.
But again: if you ever only want to size StartForm, then this should be a function in StartForm.
public SetHeight(StartFormLayout layout, IStartForm startForm)
{
switch (layout)
{
case StartFormLayout.DefaultDevice0:
if (startForm.MenuStrip.Visible)
{
startForm.Height = ...;
startForm.LabelNoRoot.Location = ...
// etc
}
else
{
...
Noticed that because of this separation of concerns the AppJobs and FormJobs don't have to know each other. AppJobs and FormJobs also don't really have to know what 'StartForm` is, only that it has the labels and buttons etc that it needs to change.
class StartForm : Form, IStartForm
{
public Label LabelNoRoot {get{return this.label1; } }
...
private void FrmStart_Load(object sender, EventArgs e)
{
AppJobs layoutdesigner = new AppJobs(...);
StartFormLayout layoutdesigner = layouter.GetPreferredLayout();
FormJobs layouter = new FormJobjs();
layouter.SetHeight(this)
}
Noticed that my form didn't have a label named "LabelNoRoot", but a Label1 that should function as a LabelNoRoot. Also: because I used types instead of string, You can be certain that I can't handle a label as if it was a button. I can't by accident try to press the label. Something that could easily been done when you were using strings to identify the items you want to layout.
Extending the comments: you just remove the new part in your FormJobs and AppJobs classes.
leaving the code in i.e. in the FormJobs class like : AppJobs appObj;
Then in your main form create at some point a FormJobs obj and an AppJobs obj and set its property.
I.e. in main Form:
AppJobs appObj = new AppJobs();
FormJobs formObj = new FormJobs();
formObj.appObj = appObj;
Tho I must say i dont like that approach you are taking with this...
You should think of another way or at least refactor your code that FormJobs does not need AppJobs methods and vice versa in a way that all calls to FormJobs and AppJobs come from your main form.

I cannot solve this simple exercise in Deitel Visual C# 2010 [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I've been studying Deitel's Visual C# 2010. I'm stuck at an exercise in the chapter about arrays. It's been bugging me for days now, and I've written the code several times now and each times something goes wrong.
The exercise is asking to develop a new reservation system for a small airline company. The capacity of an airplane is 10 seats. So you ask the customer to input 1 for First Class and 2 for Economy. Seats from 1 to 5 are for first class. Seats from 6 to 10 are for economy.
I must use a one-dimensional array of type bool to represent the seating char of the plane. Initialize all elements of the array to false to represent vacant seats (Luckily bool initializes to false anyway, because I do not know how to initialize an array). As each seat is assigned, set the corresponding element in the plane to true.
The app should never assign a seat that's already been seated. When the economy class is full, the app should ask the customer if he wants to fly in first class (and vice versa). If the customer responds with yes, assign him in the economy class (if there's an empty seat there). If there is no empty seats in economy or the customer refuses to fly in first class, then just display to him that "The next flight is after three hours!).
I'm self-studying the book. This is not an assignment or homework. I really do not wish to post the code I've written because I want a completely fresh way to solve the problem, but I'm pretty sure that I will be asked about the code, so here it is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Craps_Game
{
class AirlineReservation
{
private static bool[] seats = new bool[10];
private static void FirstClass(ref bool[] seats)
{
for(int i = 0; i < 5; i++)
{
if (seats[i] == false)
{
seats[i] = true;
Console.WriteLine("Your seat number is " + i);
break;
}
else
{
Console.WriteLine("First Class is full. Would you like to fly Economy?");
if (Console.ReadLine().ToLower() == "y")
Economy(ref seats);
else
Console.WriteLine("The next flight is in three hours!. Good bye");
}
}
}
private static void Economy(ref bool[] seats)
{
for (int i = 5; i < 10; i++)
if (seats[i] == false)
seats[i] = true;
else
{
Console.WriteLine("Economy class is full. Would you like to fly First Class");
if (Console.ReadLine().ToLower() == "y")
FirstClass(ref seats);
else
Console.WriteLine("The next flight is in three hours!. Good Bye");
}
}
public static void Reservation()
{
do
{
Console.WriteLine("Enter 1 to fly First Class");
Console.WriteLine("Enter 2 to fly Economy Class");
if (Convert.ToInt32(Console.ReadLine()) == 1)
FirstClass(ref seats);
else
Economy(ref seats);
} while (true);
}
}
}
Bear in mind that I would prefer a completely different way of solving the problem, instead of fixing this one :)
I don't want to solve the entire problem in code since you are learning, but I will show you the major things I saw in your code that is a bug.
Your Economy method was not exiting the loop if it found that a
seat was available. It needed to break.
Your else when you did not find a seat was not checking if all of the seats were taken. Checking if i == 9 will make it so it will only go to FirstClass if there are no more seats, not before.
private static void Economy(ref bool[] seats)
{
for (int i = 5; i < 10; i++)
{ // <---- Added the brackets
if (seats[i] == false)
{
seats[i] = true;
break; // <----- You forgot to break when you reserved a seat.
}
else if (i == 9) // <---- You weren't checking if all seats are taken.
{
Console.WriteLine("Economy class is full. Would you like to fly First Class");
if (Console.ReadLine().ToLower() == "y")
FirstClass(ref seats);
else
Console.WriteLine("The next flight is in three hours!. Good Bye");
}
}
}
I like the way you approach programming and your task and this site!
Therefore I would hate to write out the code for you - all the fun is getting it done by yourself. But since you are stuck let me give you a few hints:
Starting at the bottom, the first thing that comes to mind is that you are always offering both classes even when one or both are full. Here is a function header that could help to break things down even further than you already have done:
public int getFirstVacantSeatIn(int classType)
// returns 1-5 for classType=1, 6-10 for classType=2, -1 if classType is full
You can use this function to make the prompt dynamic like this:
Console.WriteLine( ( getFirstVacantSeatIn(1) >= 0 ?
"Enter 1 to fly First Class") : "First Class is full");
And you can reuse it when you try to assign the new seats..:
Another point is that you offer switching between classes when one is full without checking if the other one actually isn't full, too. I suspect that is the problem you are facing?
So you should check before offering to up- or downgrade.. The above function will help here as well. If you can re-use something, chances are that it was right to create that thing..
The secret is ever so often to break your problem down further and further until it goes away, always using/creating useful names for the sub-problems..
"I must use a one-dimensional array of type bool to represent the seating char of the plane"
"I want a completely fresh way to solve the problem"
"Bear in mind that I would prefer a completely different way of solving the problem, instead of fixing this one"
So be it! Others have given you really good advice already, but here's "fresh" way to do it.
using System;
namespace FunnyConsoleApplication
{
public class Program
{
static void Main(string[] args)
{
Airplane plane = new Airplane();
bool reserve = true;
while (reserve)
{
Console.Clear();
Console.WriteLine("Enter [1] to fly First Class ({0} vacant)", plane.VacantFirstClassSeats());
Console.WriteLine("Enter [2] to fly Economy Class ({0} vacant)", plane.VacantEconomySeats());
string input = Console.ReadLine();
switch (input)
{
case "1":
if (plane.HasFirstClassSeats)
{
plane.ReserveFirstClassSeat();
}
else if (plane.HasEconomySeats)
{
if (IsOk("No vacancy, enter [y] to fly Economy instead?"))
plane.ReserveEconomySeat();
else
reserve = false;
}
else
{
reserve = false;
}
break;
case "2":
if (plane.HasEconomySeats)
{
plane.ReserveEconomySeat();
}
else if (plane.HasFirstClassSeats)
{
if (IsOk("No vacancy, enter [y] to fly First Class instead?"))
plane.ReserveFirstClassSeat();
else
reserve = false;
}
else
{
reserve = false;
}
break;
}
Console.WriteLine();
}
Console.WriteLine("No can do, good bye!");
Console.ReadLine();
}
private static bool IsOk(string question)
{
Console.WriteLine(question);
return string.Compare(Console.ReadLine(), "y", StringComparison.OrdinalIgnoreCase) == 0;
}
}
public class Airplane
{
private readonly bool[] _seats = new bool[10];
public bool HasFirstClassSeats
{
get { return HasSeats(0); }
}
public bool HasEconomySeats
{
get { return HasSeats(5); }
}
public int VacantFirstClassSeats()
{
return GetVacant(0);
}
public int VacantEconomySeats()
{
return GetVacant(5);
}
public void ReserveFirstClassSeat()
{
Reserve(0);
}
public void ReserveEconomySeat()
{
Reserve(5);
}
private bool HasSeats(int index)
{
for (int i = index; i < index + 5; i++)
{
if (!_seats[i])
return true;
}
return false;
}
private int GetVacant(int index)
{
int count = 0;
for (int i = index; i < index + 5; i++)
{
if (!_seats[i])
count++;
}
return count;
}
private void Reserve(int index)
{
for (int i = index; i < index + 5; i++)
{
if (!_seats[i])
{
_seats[i] = true;
break;
}
}
}
}
}
Which gives you

Categories

Resources