Passing an argument to a method in C# - c#

So, this program functions as it should, but I don't think the code is very "clean," so I'm looking for suggestion. Two of the big issues I have:
For the method public double temperatureInFahrenheit, I want to pass the argument celsiusTemperature to the function instead of having to redeclare the variable, convert it from the text to the double, etc. Whenever I attempt to try that I get an error in the MessageBox.Show when I try to call the function myAirport.temperatureInFahrenheit (I don't specifically remember what the error is, so I'll have to recode if that's needed). Any thoughts on what I can do to make this work?
The MessageBox.Show in the public partial class Form1 seems like messy code to me. What I'd like to do is write a method in internal class Airport, where the necessary arguments are passed to the method, and then just do something like MessageBox.Show(myAirport.message()), but I'm assuming if I tried that I'd get the same error as I'm getting for 1. Any thoughts?
Again, the code is completely functional and works and meets the required specifications, but I don't like just having functional code, I like having functional code that's "pretty." Note: I don't have comments for any of the methods, variables, etc. I'm putting those in right now, but thought I'd try and get some feedback first.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string airportName;
double celsiusTemperature, elevation;
if (String.IsNullOrEmpty(txtAirport.Text)) {
MessageBox.Show("You did not enter a name for the airport. Please enter a name for the airport.");
return;
} else
{
airportName = Convert.ToString(txtAirport.Text);
}
if (Double.TryParse(txtTemperature.Text, out celsiusTemperature))
{
if (celsiusTemperature > 50 || celsiusTemperature < -50)
{
MessageBox.Show("The value you entered for temperature is outside the acceptable range. Please reenter the information");
Application.Restart();
}
}
else
{
MessageBox.Show("You did not enter a numeric value for the temperature. Please enter a valid, i.e. numeric, value for the temperature.");
return;
}
if (Double.TryParse(txtElevation.Text, out elevation))
{
if (elevation > 12000 || elevation < -300)
{
MessageBox.Show("The value you entered for elevation is outside the acceptable range. Please reenter the information.");
Application.Restart();
}
}
else
{
MessageBox.Show("You did not enter a numeric value for the elevation. Please enter a valid, i.e. numeric, value for the elevation.");
return;
}
Airport myAirport = new Airport(airportName, celsiusTemperature, elevation);
MessageBox.Show("The airport name is: " + myAirport.airportName(airportName) + Environment.NewLine + "The Celsius temperature is: " + myAirport.celsiusTemperature(celsiusTemperature)
+ Environment.NewLine + "The Fahrenheit temperature is: " + myAirport.temperatureInFahrenheit(celsiusTemperature) + Environment.NewLine + "The elevation is: " + myAirport.elevation(elevation));
}
private void button2_Click(object sender, EventArgs e)
{
Close();
}
}
internal class Airport
{
private string airportName1;
private double celsiusTemperature1;
private double elevation1;
public Airport(string airportName1, double celsiusTemperature1, double elevation1)
{
this.airportName1 = airportName1;
this.celsiusTemperature1 = celsiusTemperature1;
this.elevation1 = elevation1;
}
public string airportName(string airportName1)
{
return airportName1;
}
public double celsiusTemperature(double celsiusTemperature1)
{
return celsiusTemperature1;
}
public double elevation(double elevation1)
{
return elevation1;
}
public double temperatureInFahrenheit(double celsiusTemperature1)
{
double fahrenheitTemperature = 0;
fahrenheitTemperature = celsiusTemperature1 * (1.8) + 32;
return fahrenheitTemperature;
}
}
}

Here is an example of how you could simplify the Airport class:
public class Program
{
static void Main(string[] args)
{
var airport = new Airport { AirportName = "JFK", Temperature = 28.5 };
Console.WriteLine(airport.ToString());
}
}
public class Airport
{
private string _airportName;
private double _temperatureInCelsius;
private double _temperatureInFahrenheit;
public string AirportName
{
get
{
return _airportName;
}
set
{
if (string.IsNullOrWhiteSpace(value))
{
throw new Exception("You did not enter a name for the airport. Please reenter the information.");
}
_airportName = value;
}
}
public double Temperature
{
get
{
return _temperatureInCelsius;
}
set
{
if (value > 50 || value < -50)
{
throw new Exception("The value you entered for temperature is outside the acceptable range. Please reenter the information");
}
_temperatureInCelsius = value;
_temperatureInFahrenheit = _temperatureInCelsius *(1.8) + 32;
}
}
public override string ToString()
{
return string.Format(
"The airport name is: {0}\r\nThe Celsius temperature is: {1}\r\nThe Fahrenheit temperature is: {2}", _airportName, _temperatureInCelsius, _temperatureInFahrenheit);
}
}
Notice that the AirportName setter validates the passed airport name (via value) and if it's not valid, then an exception is thrown.
I will leave the other property - elevation, as an excercise for you to finish.
Regards the Message.Show you can do this:
Message.Show(airport.ToString());
The ToString() method return a string that describes the airport.
My code is a suggestion, you don't have to use it exactly (i.e. you might not like throwing an exception from the setters. Instead you could validate the values before creating an Airport instance.), but hopefully it will guide you.

There are many ways to separate the different requirements, so i try to give the idea.
First, a simple airport data class with properties and inside validation (That means invalid instances are possible):
internal class Airport
{
private string _airportName;
private double _celsiusTemperature;
private double _elevation;
public Airport(string airportName, double celsiusTemperature, double elevation)
{
this._airportName = airportName;
this._celsiusTemperature = celsiusTemperature;
this._elevation = elevation;
}
public string AirportName
{
get
{
return _airportName;
}
set
{
_airportName = value;
}
}
public double CelsiusTemperature
{
get
{
return _celsiusTemperature;
}
set
{
_celsiusTemperature = value;
}
}
public double Elevation
{
get
{
return _elevation;
}
set
{
_elevation = value;
}
}
public double TemperatureInFahrenheit
{
get
{
return _celsiusTemperature * (1.8) + 32.0;
}
set
{
if (value != 32.0)
{
_celsiusTemperature = (value - 32.0) / (1.8);
}
else
{
_celsiusTemperature = 0.0;
}
}
}
public bool IsValid(out string errorMessage)
{
bool result = false;
bool ok = true;
errorMessage = "";
if (String.IsNullOrEmpty(_airportName))
{
ok = false;
errorMessage = "You did not enter a name for the airport.";
}
if (_celsiusTemperature > 50 || _celsiusTemperature < -50)
{
ok = false;
errorMessage = "The value you entered for temperature is outside the acceptable range.";
}
if (_elevation > 12000 || _elevation < -300)
{
ok = false;
errorMessage = "The value you entered for elevation is outside the acceptable range.";
}
result = ok;
return result;
}
}
Note that Fahrenheit is a calculated value based on Celsius. It could be vice versa.
Also note the validation. The airport class is accepting all values, only the type of the fields is defined. It does the semantic check (business logic).
Now, how to use it:
private void button1_Click(object sender, EventArgs e)
{
string airportName;
double celsiusTemperature;
double elevation;
// Get data from controls and do syntactic checks
bool ok = true;
airportName = txtAirport.Text;
ok = Double.TryParse(txtTemperature.Text, out celsiusTemperature);
if (!ok)
{
// Error
MessageBox.Show("The value you entered for temperature is not a number!", "Error");
}
ok = Double.TryParse(txtElevation.Text, out elevation);
if (!ok)
{
// Error
MessageBox.Show("The value you entered for elevation is not a number!", "Error");
}
if (ok)
{
// Create the instance of the data class and do semantic checks
Airport myAirport = new Airport(airportName, celsiusTemperature, elevation);
string errorMessage;
if (!myAirport.IsValid(out errorMessage))
{
// Error
MessageBox.Show(errorMessage + " Please reenter the information", "Error");
}
else
{
// Ok, data is valid. Continue normal work...
MessageBox.Show("The airport name is: " + myAirport.AirportName + Environment.NewLine +
"The Celsius temperature is: " + myAirport.CelsiusTemperature + Environment.NewLine +
"The Fahrenheit temperature is: " + myAirport.TemperatureInFahrenheit + Environment.NewLine +
"The elevation is: " + myAirport.Elevation);
}
}
You get the data from controls and do syntactic checks. If everthing is allright, you let the class do the semantic checks itself. Here it will give a string as message details, but many otehr ways are passible.
So, in the end, if a syntatic error occurs, the user gets a message and can continue. If a semantic error occurs, the user gets a message and can continue. If everything is ok, you can operate an valid data.
Hope this helps...

I think, you should refactor your code in the following ways:
Airport seems to be a data class. So it should not know about the TextBox's of Form1. So, try to separate them.
For easy access, you should think of properties (or getter/setter methods) for your fields in the airport class.
You are checking the data when you are reading it out of the airport class. What if you are never reading it or using it inside before? So, you should separate this.
If you estimate an error, you are restarting the complete application. That is normaly not a good deal to the user. So try to show the error and let the user do some corrections. And check again, and so on...
If Airport is data class, then you can have to properties for Celsius and Fahrenheit. No matter what representation you have internaly.
I would like to give these hints without code first, so you can think and try for yourself before looking at a complete solution. Then, if you get stuck somewhere, i (and others) will give more concrete tips.
So, good luck for you...

Related

Winforms Get/Set between classes causing error - Object Reference Error

Having trouble solving this one. Might just be burned out tbh, ive been at this for hours. I am new to Classes in C# and it is kicking the crap out of me trying to pass data between classes. I know there are steps that I am missing, but microsoft docs is not being very helpful with my question so here goes.
Trying to pass values from once class to another. The error code I am getting is CS0120
This is the format of what i am using within the first class
private void btn_Compute_Click(object sender, EventArgs e)
{
decimal dL = Validator(box_Left.Text);
decimal dR = Validator(box_Right.Text);
decimal Answer = 0;
string op = "";
if (rad_Add.Checked == true)
{
MathFirstClass.Left = dL;
MathFirstClass.Right = dR;
op = " + ";
}
}
and the code inside the other class that I am trying to send the data to looks like this
decimal left;
decimal right;
decimal Answer;
public decimal Left
{
get { return left; }
set { left = value; }
}
public decimal Right
{
get { return right; }
set { right = value; }
}
public decimal Add_Operands
{
get
{
Answer = Left + Right;
return Answer;
}
}
Also if anyone wants to fill me in on how to send the answer back to the first class that would also be a great help.
You create an instance of your class.
private void btn_Compute_Click(object sender, EventArgs e)
{
decimal dL = Validator(box_Left.Text);
decimal dR = Validator(box_Right.Text);
decimal Answer = 0;
string op = "";
//****************************************
MathFirstClass mathFirstClass = new MathFirstClass();
if (rad_Add.Checked == true)
{
mathFirstClass.Left = dL;
mathFirstClass.Right = dR;
op = " + ";
}
}

Initialize value in dictionary only once, then just update

I'm currently working to track packages when they move between bays, these changes happen every dozen or so seconds, so I have several threads accessing some dictionaries. The idea is that the first few times, I will not have the lastBay value (the program is just starting), but when A == B for the first time, I save the value of A (the bay that the pacakage has landed into, therefore the last bay at which it has been) and then just update said value every time A == B again.
private void in_bay()
{
String line_type = getDictionaryValues("global", "line_type").getStringValue();
bool result = false;
switch (line_type)
{
case "A_1":
int A = getVariableFromBucket("A_Act").getIntValue();
int B = getVariableFromBucket("A_Next").getIntValue();
result = A == B ? true : false;
if (result)
{
setDictionaryValues("global", "lastBay", new Variable("UNSIGNED8") { binaryValue = Utils.intToByteArray(A) });
}
break;
}
setVariableInBucket("IN_BAY", BitConverter.GetBytes(result));
log("IN_BAY flag in BUCKET: " + getVariableFromBucket("IN_BAY").getBoolValue(), DEBUG);
if (getDictionaryValues("global", "lastBay").binaryValue != null)
{
log("lastBay value in global: " + getDictionaryValues("global", "lastBay").getIntValue(), DEBUG);
}
else
{
log("undefined bay",DEBUG);
}
}
I have a getDictionaryValue() function that returns the variables (or an empty one if it's not in the dictionary):
public Variable getDictionaryValues(String DictionaryName, String VarName)
{
try
{
return functionDictionary[DictionaryName][VarName];
}
catch (Exception)
{
Variable emptyVariable = new Variable()
{
additionalInfo = null,
arrivalTime = 0,
binaryValue = null,
numBits = 0,
signed = false,
varType = null
};
return emptyVariable;
}
}
and a setDictionaryValue() function that actually sets the values to the dictionary selected:
public void setDictionaryValues(String DictionaryName, String VariableName, Variable VaValue)
{
try
{
lock (GlobalConstants.filtersLock)
{
if (!functionDictionary.ContainsKey(DictionaryName))
{
functionDictionary.Add(DictionaryName, new Dictionary<String, Variable>());
}
if (!functionDictionary[DictionaryName].ContainsKey(VariableName))
{
functionDictionary[DictionaryName].Add(VariableName, Value);
}
else
{
functionDictionary[DictionaryName][VariableName] = Value;
}
}
}
catch (Exception e)
{
log("An error has ocurred when setting values to functionDictionary: "+ e,DEBUG);
throw new Exception(e.ToString());
}
}
The problem is that the first time A == B It logs correctly the values being received, but when the values change again (the package starts moving again) the code no longer displays the values of lastBay, as if the dictionary global no longer has a value for lastBay. I attach an image with a reference as to the expected results and the results obtained:
What am I missing here?
From the comment thread, it looks like the problem is that in_bay is being called on different object instances, and functionDictionary is a non-static field, so you're dealing with different dictionary instances each time.
I just want to take the opportunity to point out how much simpler your code could be if you just used classes and variables rather than adding dictionaries and "Variable" objects as a layer of abstraction.
private void in_bay()
{
string? line_type = BayState.line_type;
bool result = false;
if(line_type == "A_1")
{
int A = Bucket.A_Act;
int B = Bucket.A_Next;
result = A == B;
if (result)
{
BayState.lastBay = A;
}
}
Bucket.IN_BAY = result;
log("IN_BAY flag in BUCKET: " + Bucket.IN_BAY, DEBUG);
if (BayState.lastBay != null)
{
log("lastBay value in global: " + BayState.lastBay.Value, DEBUG);
}
else
{
log("undefined bay", DEBUG);
}
}
I can pretty much guarantee whatever "business need" is driving the desire for dictionaries and such can be accomplished in another way that still allows you to write clean and less error-prone code.

IF Boolean statement order of input validation

I have to validate some inputs the user makes, and send a error message
this is what I got so far
// probes the methods to check for validity.
private void btnCalculate_Click(object sender, EventArgs e)
{
if (!(ValidWidth(float.Parse(txtWidth.Text))))
{
return;
}
if (!(ValidLength(float.Parse(txtLength.Text))))
{
return;
}
if (!(ValidDepth(float.Parse(txtAvgDepth.Text))))
{
return;
}
}
My problem is when I enter the values into Length, Width, and Depth. It only does it in order..what I mean is if I don't enter a width and leave it blank and put in length and depth it gives me a unhandled expection.
here are my methods
/** Created a boolean method to test if the written width is valid OR not valid **/
private bool ValidWidth(float Width1) {
float Width = float.Parse(txtWidth.Text);
{
if (Width >= 2 & Width <= 20)
{
return true;
}
else
{
string Title = "Data Invalid";
string Msg = "Width Measurement is invalid \n Place enter a value between 2 and 20";
DialogResult Response;
Response = MessageBox.Show(Msg, Title, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return false;
}
}
}
/** Created a boolean method to test if the written legnth is valid OR not valid **/
private bool ValidLength(float Length1)
{
float Length = float.Parse(txtLength.Text);
{
if (Length >= 5 & Length <= 50)
{
return true;
}
else
{
string Title = "Data Invalid";
string Msg = "Legnth Measurement is invalid \n Place enter a value between 5 and 50";
DialogResult Response;
Response = MessageBox.Show(Msg, Title, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return false;
}
}
}
/** Created a boolean method to test if the written legnth is valid OR not valid **/
private bool ValidDepth(float Depth1)
{
float Depth = float.Parse(txtAvgDepth.Text);
if (Depth >= 2 & Depth <= 4)
{
return true;
}
else
{
string Title = "Data Invalid";
string Msg = "Average Depth Measurement is invalid \n Place enter a value between 2 and 4";
DialogResult Response;
Response = MessageBox.Show(Msg, Title, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return false;
}
}
The Parse method will throw an exception if you feed it an empty string. You should catch that exception, or use TryParse.
You messed up every thing in your code. first there is a method float.TryParse which attempts to convert your string into float number. but it will not throw an exception if conversion failed. instead it gives a boolean value which tells parse succeeded or not.
I think this is better.
private void btnCalculate_Click(object sender, EventArgs e)
{
if(!ValidateWidth(txtWidth.Text) ||
!ValidateLength(txtLength.Text) ||
!ValidateDepth(txtAvgDepth.Text)) // if any of these failed
{
MessageBox.Show(Msg, Title, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
I write ValidateWidth for you as an example.
private string Title = "Data Invalid";
private string Msg;
private bool ValidateWidth(string input)
{
float width;
if(float.TryParse(input, out width))
{
if (Width >= 2 && Width <= 20)
{
return true;
}
}
Msg = "Width Measurement is invalid \n Place enter a value between 2 and 20";
return false;
}

Combobox value (binded with an enum) in an instance

Good day lads!
I've got a question.
I think I'll be saving loads of text if you would see my form, so here we go!
Form:
private void Form1_Load(object sender, EventArgs e)
{
/*
Held h1 = new Held("Tank", Lanes.Top);
Held h2 = new Held("ADC", Lanes.Bot);
Held h3 = new Held("Support", Lanes.Bot);
listBox1.Items.Add(h1);
listBox1.Items.Add(h2);
listBox1.Items.Add(h3);
*/
//Data koppelen
cbRol.DataSource = Enum.GetValues(typeof(Lanes));
}
private void btnAanmaken_Click(object sender, EventArgs e)
{
int getal;
if (CheckEmptyFields())
{
MessageBox.Show("Vul alle velden in!");
}
else
{
if (CheckMovementSpeedIsInt(out getal))
{
string naamHero = tbNaamHero.Text;
Lanes lane = ???
int mSpeedHero = getal;
Held nieuwHeld = new Held(naamHero, lane, getal);
}
}
}
private bool CheckMovementSpeedIsInt(out int getal)
{
return Int32.TryParse(tbMoveSpeed.Text, out getal);
}
private bool CheckEmptyFields()
{
return tbNaamHero.Text == null || tbMoveSpeed.Text == null || cbRol.SelectedItem == null;
}
Held:
class Held
{
private string Naam;
private Lanes Lane;
int MSpeed;
public Held(string aNaam, Lanes aLane, int aMSpeed)
{
this.Naam = aNaam;
this.Lane = aLane;
this.MSpeed = aMSpeed;
}
public override string ToString()
{
return this.Naam + " " + this.Lane.ToString();
}
}
}
Lanes:
enum Lanes
{
Top,
Mid,
Bot,
Jungle
}
Alright! So as you can see I have combined the enum with the ComboBox. I'd like to put the selected value (when the user has pressed the button "Aanmaken/Create") in the instance.
How am I able to convert the object (from ComboBox) to a type (Lanes)?
If I haven't clarified enough, just give me a heads up!
PS: The "???" in the code is the place where I'm not sure what to put since that's the question hehe.
Just use the following:
Lanes lane = (Lanes)cbRol.SelectedIndex;
This works due to enum is typeof int, so your Top is actually 0, and so on...
You can parse your Enum.Parse
Lanes lange = (Lanes) Enum.Parse(typeof(Lanes), cbRol.SelectedItem.ToString(), true);
This also works for index
Lanes lange = (Lanes) Enum.Parse(typeof(Lanes), cbRol.SelectedIndex.ToString(), true);

Error Providers

I am trying to apply error providers to a text box,
The user cannot enter more than 25 characters
The textbox cannot be left blank
private void txtNameandSurn_TextChanged(object sender, EventArgs e)
{
txtNameandSurn.MaxLength = 25;
if (txtNameandSurn.Text == "")
{
txtNameandSurn.BackColor = Color.White;
errorProvider1.SetError(txtNameandSurn, "Cannot be blank!");
}
else
{
txtNameandSurn.BackColor = Color.Red;
errorProvider1.SetError(txtNameandSurn, "");
}
if (txtNameandSurn.Text.Length >= txtNameandSurn.MaxLength)
{
errorProvider1.SetError(txtNameandSurn, "Cannot input more than 25 characters!");
}
else if (txtNameandSurn.Text.Length < txtNameandSurn.MaxLength)
{
errorProvider1.SetError(txtNameandSurn, "");
}
}
The issue I have is with the character input, the error provider shows up but when i press another key the icon for the errorprovider disappears but the character is not inputted which is good. How do I keep the error provider icon showing?
You should rewrite your conditions in a logical manner. Every case where there is not a correct input should have it's definition and have the correct error set. In the other case (input is correct), remove the error. Simplified it should look like this:
if (String.IsNullOrEmpty(textBox1.Text))
{
errorProvider1.SetError(textBox1, "Cannot be blank!");
}
else if(textBox1.Text.Length >= textBox1.MaxLength)
{
errorProvider1.SetError(textBox1, "Cannot input more than 25 characters!");
}
else
{
errorProvider1.SetError(textBox1, "");
}
This way, you can add more conditions easiliy. If you want for example that the tekst doesn't contain a #, simply add following statement:
if(textBox1.Contains("#"))
{
errorProvider1.SetError(textBox1, "Cannot contain a '#'!");
}
For great readability and flexibility for adding new rules, I prefer this syntax. Note that this also lends itself to creating reusable sets of rules for specific data types.
This untested code should solve your problem with the errorProvider, if I understood it.
class Rule
{
public Func<string, bool> Test { get; set; }
public string Message { get; set; }
}
private void txtNameandSurn_TextChanged(object sender, EventArgs e)
{
var rules = new List<Rule>()
{
new Rule() { Test = s => !String.IsNullOrEmpty(s), Message="String cannot be blank." },
new Rule() { Test = s => (s.Length <= txtNameandSurn.MaxLength), Message="String cannot be longer than " + txtNameandSurn.MaxLength },
new Rule() { Test = s => !s.Contains("#"), Message = "String cannot contain a hash character." }
};
var isValid = rules.All(r => r.Test(txtNameandSurn.Text));
string[] message;
if (!isValid)
{
message = rules.Where(r => r.Test(txtNameandSurn.Text) == false).Select(r => r.Message);
}
errorProvider1.SetError((message.Length > 0) ? (string.Join(';', message)) : "");
}

Categories

Resources