I created a class I named Cashiers(shown below). I can create new instances through code no problem. What I can not do is have a user input a string value into a string variable I named CashierLOgInNName. So if the user inputs the value as DSPR I want to be able to create a new cashier object by that name, or the equivalent of
Cashiers DSPR = new Cashiers();
I've included the parts of my code that pertain to this question. Ideally if I could have the line or lines of code that would enable me to have this work and why that be excellent.
public class Cashiers
{
public int CashierID;
public int Password;
public string FirstName;
public string LastName;
public void SetCashiers(int CashierID, int Password,string FirstName, string LastName )
{
this.CashierID = CashierID;
this.Password = Password;
this.FirstName = FirstName;
this.LastName = LastName;
}
public void SetNewCashier(int CashierID, int Password, string FirstName, string LastName)
{
//Cashiers NewCashier = new Cashiers();
this.CashierID = CashierID;
this.Password = Password;
this.FirstName = FirstName;
this.LastName = LastName;
}
}
Console.WriteLine("enter New Log in name");
string CashierLOgInNName = Console.ReadLine();
To more directly answer with your example, building on the best method of using a dictionary (aka Key Value Pair):
Dictionary<String,Cashiers) dictCashiers = new Dictionary<String,Cashiers>();
Console.WriteLine("Enter new login name:");
String CashierLogInName = Console.ReadLine();
Cashiers newCashier = new Cashiers();
dictCashiers.Add(CashierLogInName,newCashier);
//replace constants in the next line with actual user's data, probably input from more ReadLine queries to user?
dictCashiers[CashierLogInName].SetNewCashier(1,2,"Jane","Doe");
You can see dictCashiers[CashierLogInName] accomplishes what I believe you are looking for, and retrieves the Cashiers object associated with that Login ID.
It sounds to me like what you're really looking for is either a database (which is too broad to answer here) or a dictionary.
In a dictionary, you'll be able to store your Cashier objects keyed by a string. You aren't affecting the name of the variable, of course, but you can still use that name in a theoretical context to get at what you're looking for. If you did actually change the name, that would lead to a need for reflection, which is messy and slow.
private Dictionary<string, Cashiers> dict = new Dictionary<string, Cashiers>();
private void Save(string name, [probably some other details?])
{
dict[name] = new Cashiers(); // or whatever
}
private void Print(string name)
{
Console.WriteLine(dict[name]);
}
private void PrintAll()
{
Console.WriteLine(string.Join(Environment.NewLine, dict.Select(c => c.Key + "\t" + c.Value.ToString()));
}
Obviously my implementation here leaves something to be desired, but it shows how you can use it.
Related
I am trying to write a c# program to make a Bank system with users creating bank accounts. And I want to be able to save these accounts given through the console to an excel file so it can retrieve them later if needed.
I followed a tutorial on how to do that and made minor adjustments to the code but when I try to run the program with some test accounts it writes them as blank accounts with no data and I can't see why it would do this.
public class Account
{
protected static int count = 1;
protected int accountNum;
protected string fName;
protected string lName;
protected int idNumber;
protected string address;
protected DateOnly birthday = new();
protected bool flag;
Savings.SpendingRef = Spendings(accountNum);
public Account(string fn, string ln, int idnum, string add, DateOnly bd)
{
//System.Threading.Interlocked.Increment(ref count);
this.accountNum = count++;
fName = fn;
lName = ln;
idNumber = idnum;
address = add;
birthday = bd;
flag = false;
}
public int AccountNum{get;}
public string FName{get; set;}
public string LName{get; set;}
public int IdNumber{get; set;}
public string Address{get; set;}
public DateOnly Birthday{get;}
public bool Flag{get; set;}
public string GetAccount(){
string s = "--------------------------------\n";
s += "Account Number :" + accountNum + "\nAccount belonging to :" + fName + " " + LName + "\nID Number :" + IdNumber + "\nAddress :" + address + "\nBirthday : " + birthday + "\nAccount blocked? :" + flag;
s += "\n--------------------------------\n";
Console.WriteLine(s);
return s;
}
}
Here is how the account class is implemented
static async Task Main(string[] args)
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
var file = new FileInfo(#"..\..\..\SavedAccounts.XLSX");
//Other code not important
var Accounts = GetSetupData();
await SaveExcelFile(Accounts, file);
}
private static List<Account> GetSetupData()
{
List<Account> output = new()
{
new Account("Jerome", "Dupret", 42069, "68 rue des ecoles", new DateOnly(1999, 08, 10)),
new Account("Deborah", "Pierre", 69420, "68 rue des ecoles", new DateOnly(2000, 02, 18)),
};
return output;
}
Here are the test accounts I want to be inserted.
private static async Task SaveExcelFile(List<Account> accounts, FileInfo file)
{
DeleteIfExists(file);
using var package = new ExcelPackage(file);//basically a Dispose method to get rid of any useless data
var ws = package.Workbook.Worksheets.Add("AccountData");
var range = ws.Cells["A1"].LoadFromCollection(accounts, true);
range.AutoFitColumns();
await package.SaveAsync();
}
And this is where the call and the write function itself happen.
I used console Writes at every step to check if the accounts lose info at any time but they never do, I'm confused as to why it isn't sent properly.
It looks like you never set your public properties such as Fname, either you need to set them in the constructor or make the getter get the protected field.
for example
protected string fName;
public string FName
{
get
{
return fName;
}
set
{
fName = value;
}
}
This needs to be done on all the public fields so they all get the field that gets set in the constructor.
The set is not required but if you dont use it FName and fName can become diffrent values since fName is only set in the constructor.
I am looking for the solution to save and reload what I enter into my text boxes or what I filled in my ComboBoxes. Here is the code I current have, but I am not 100% sure it is all correct. I am only posting the code I need help with. If you need the entire batch of code I can post it, but I did not think it was relevant to what I am asking for. This is what I have done so far, which I am not sure it will actually save the info that is typed in, and I am completely unsure as to what to put in the loadLastBtn method. I Just want it to be saved to memory even when the textbox texts have changed and then I want to be able to retrieve that from memory when I hit the loatLastbtn.
private void submitBtn_Click(object sender, EventArgs e)
{
List<Members> lstMembers = new List<Members>();
if (string.IsNullOrEmpty(firstNameTxt.Text) || string.IsNullOrEmpty(lastNameTxt.Text)
|| string.IsNullOrEmpty(userNameTxt.Text) ||
string.IsNullOrEmpty(passwordTxt.Text) || string.IsNullOrEmpty(confPassTxt.Text)
|| string.IsNullOrEmpty(majorBox.Text) || string.IsNullOrEmpty(specialtyBox.Text))
{
MessageBox.Show("You must enter in all fields before moving forward");
}
else
{
//would this save my data to the array and how can I reload this?
Members m1 = new Members(firstNameTxt.Text, lastNameTxt.Text, userNameTxt.Text,
passwordTxt.Text, confPassTxt.Text, majorBox.Text,
specialtyBox.Text);
lstMembers.Add(m1);
}
}
private void loadLastBtn_Click(object sender, EventArgs e)
{
for(int i = 0; i < lstMembers.Count; i++)
{
firstNameTxt.Text = lstMembers[i].FirstName;
lastNameTxt.Text = lstMembers[i].LastName;
userNameTxt.Text = lstMembers[i].UserName;
passwordTxt.Text = lstMembers[i].Password;
confPassTxt.Text = lstMembers[i].ConfPassword;
majorBox.Text = lstMembers[i].Major;
specialtyBox.Text = lstMembers[i].Specialty;
}
}
Here is the user object I created. I am just unsure how to call it now when I want to save and reload.
class Members
{
private string firstName;
private string lastName;
private string userName;
private string password;
private string confPassword;
private string major;
private string specialty;
public Members(string firstName, string lastName, string userName, string password,
string confPassword, string major, string specialty)
{
this.firstName = firstName;
this.lastName = lastName;
this.userName = userName;
this.password = password;
this.confPassword = confPassword;
this.major = major;
this.specialty = specialty;
}
public string FirstName
{
get
{
return firstName;
}
set
{
FirstName = firstName;
}
}
public string LastName
{
get
{
return lastName;
}
set
{
LastName = lastName;
}
}
public string UserName
{
get
{
return userName;
}
set
{
UserName = userName;
}
}
public string Password
{
get
{
return password;
}
set
{
Password = password;
}
}
public string ConfPassword
{
get
{
return confPassword;
}
set
{
ConfPassword = confPassword;
}
}
public string Major
{
get
{
return major;
}
set
{
Major = major;
}
}
public string Specialty
{
get
{
return specialty;
}
set
{
Specialty = specialty;
}
}
I would suggest creating a 'POCO' (Plain old c# object) or struct that will hold your data.
When you want to save it, just serialize it to disk (either using MS Serialization (more here, or by using JSON serialize method (more here).
This way, you don't worry about what the user might have entered (less worries about sanitizing the data and making sure he didn't use your escape character).
Well, most common way to do it without any hard implementation would be writing all of your variables with ';' between them within a file. Then read it from file and re-assign it into your form elements when user press down a button.Check this guide to how to write into file:
https://msdn.microsoft.com/en-us/library/8bh11f1k.aspx
I'll give an simple example:
For example my form consist of username and password TextFields. After user entered them and pressed login, I save them into file for next time user logs in, this method will save it.
public void save(){
String username = txtUsername.Text;
String password = txtPassword.Text;
File.WriteAllText(path, username + ";" + password);
}
public void load(){
String parameters = File.ReadAllText(path);
String[] splitted = parameters.split(';');
txtUsername.Text = splitted[0];
txtPassword.Text = splitted[1];
}
I have a string with the following structure:
Student Name________AgeAddress_______________________Bithday___Lvl
Example:
Jonh Smith 016Some place in NY, USA 01/01/2014L01
As you can see, there is no delimited character like | or ,
Also, there is no space between fields (if you check, there is no space between Age/Address and Birthday/Level.
The size of each field is static so if data's length is less then it will contains white spaces.
I have a class that need to be filled with that information:
public class StudentData
{
public char[] _name = new char[20];
public string name;
public char[] _age = new char[3];
public string age;
public char[] _address = new char[30];
public string address;
public char[] _bday = new char[10];
public string bday;
public char[] _level = new char[3];
public string level;
}
Is there any way to do this automatically and dynamically?
I mean I really don't want to code like this:
myClass.name = stringLine.substring(0,19);
myClass.age = stringLine.substring(20,22);
That's because I have way more fields that the ones added in this example & way more string lines with other different data.
Update: There were supposed to be a lot of spaces between "Smith" and "016", but I don't know how to edit it.
Update2: If I use StringReader.Read() I can evade to use substring and indexes, but it isn't still so dynamically because I would need to repeat those 3 lines for each field.
StringReader reader = new StringReader(stringLine);
reader.Read(myClass._name, 0 myClass._name.Length);
myClass.name = new string(myClass._name);
Given your requirement I came up with an interesting solution. All be-it it may be more complex and longer than using the String.SubString() method as stated.
However this solution is transferable to other types and other string. I used a concept of Attributes, Properties, and Reflection to parse a string by a Fixed Length and setting the class Properties.
Note I did change your StudentData class to follow a more conventional coding style. Following this handy guide on MSDN: http://msdn.microsoft.com/en-us/library/xzf533w0(v=vs.71).aspx
Here is the new StudentData class. Note it uses the properties as opposed to fields. (Not discussed here).
public class StudentData
{
string name;
string age;
string address;
string bday;
string level;
[FixedLengthDelimeter(0, 20)]
public string Name { get { return this.name; } set { this.name = value; } }
[FixedLengthDelimeter(1, 3)]
public string Age { get { return this.age; } set { this.age = value; } }
[FixedLengthDelimeter(2, 30)]
public string Address { get { return this.address; } set { this.address = value; } }
[FixedLengthDelimeter(3, 10)]
public string BDay { get { return this.bday; } set { this.bday = value; } }
[FixedLengthDelimeter(4, 3)]
public string Level { get { return this.level; } set { this.level = value; } }
}
Note on each of the properties there is an Attribute called FixedLengthDelimeter that takes two parameters.
OrderNumber
FixedLength
The OrderNumber parameter denotes the order in the string (not the position) but the order in which we process from the string. The second parameter denotes the Length of the string when parsing the string. Here is the full attribute class.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class FixedLengthDelimeterAttribute : Attribute
{
public FixedLengthDelimeterAttribute(int orderNumber, int fixedLength)
{
this.fixedLength = fixedLength;
this.orderNumber = orderNumber;
}
readonly int fixedLength;
readonly int orderNumber;
public int FixedLength { get { return this.fixedLength; } }
public int OrderNumber { get { return this.orderNumber; } }
}
Now the attribute is simple enough. Accepts the two paramters we discussed eariler in the constructor.
Finally there is another method to parse the string into the object type such as.
public static class FixedLengthFormatter
{
public static T ParseString<T>(string inputString)
{
Type tType = typeof(T);
var properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public); //;.Where(x => x.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false).Count() > 0);
T newT = (T)Activator.CreateInstance(tType);
Dictionary<PropertyInfo, FixedLengthDelimeterAttribute> dictionary = new Dictionary<PropertyInfo, FixedLengthDelimeterAttribute>();
foreach (var property in properties)
{
var atts = property.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false);
if (atts.Length == 0)
continue;
dictionary[property] = atts[0] as FixedLengthDelimeterAttribute;
}
foreach (var kvp in dictionary.OrderBy(x => x.Value.OrderNumber))
{
int length = kvp.Value.FixedLength;
if (inputString.Length < length)
throw new Exception("error on attribute order number:" + kvp.Value.OrderNumber + " the string is too short.");
string piece = inputString.Substring(0, length);
inputString = inputString.Substring(length);
kvp.Key.SetValue(newT, piece.Trim(), null);
}
return newT;
}
}
The method above is what does the string parsing. It is a pretty basic utility that reads all the properties that have the FixedLengthDelimeter attribute applied a Dictionary. That dictionary is then enumerated (ordered by OrderNumber) and then calling the SubString() method twice on the input string.
The first substring is to parse the next Token while the second substring resets the inputString to start processing the next token.
Finally as it is parsing the string it is then applying the parsed string to the property of the class Type provided to the method.
Now this can be used simply like this:
string data1 = "Jonh Smith 016Some place in NY, USA 01/01/2014L01";
StudentData student = FixedLengthFormatter.ParseString<StudentData>(data1);
What this does:
Parses a string against property attributes in a fixed length format.
What this does not do:
It does convert the parsed strings to another type. Therefore all the properties must be a string. (this can be easily adapted by adding some type casting logic in).
It is not well tested. This is only tested against a few samples.
It is not by all means the only or best solution out there.
You could use FileHelpers library (NuGet).
Just define the structure of your input file with attributes:
[FixedLengthRecord]
public class StudentData
{
[FieldFixedLength(20)]
[FieldTrim(TrimMode.Right)]
public string name;
[FieldFixedLength(3)]
public string age;
[FieldFixedLength(30)]
[FieldTrim(TrimMode.Right)]
public string address;
[FieldFixedLength(10)]
public string bday;
[FieldFixedLength(3)]
public string level;
}
Then simply read the file using FileHelperEngine<T>:
var engine = new FileHelperEngine<StudentData>();
var students = engine.ReadFile(filename);
I am using properties to allow a single string name to be added to the class Details, I want the property to only accept the string if it can be split into two parts.
The two parts would be the firstName and the LastName. However if the resulting split has 1,0 or more than 2 strings in the array then the input should be deemed invalid and I want to throw a error to whatever code called the property in the first place.
Can error handling be done on properties like this?
If not then what of the following is the preferred way to get data into a class whilst checking for correctness:
Use a method inside the class Details to handle error inputs, make that method boolean.
Continue using properties but have the error checking done by the code that calls the property. I don't like this because I want all the error checking code to be self-contained in the Details class.
.
class Details
{
private string firstName, lastName;
public string Name
{
// name
get { return firstName + " " + lastName; }
set
{
string name = value;
string[] nameArray = name.Split(' ');
firstName = nameArray[0];
lastName = nameArray[1];
}
}
}
EDIT: I am mostly interested in what of the three options is concidered best pratice:
Error check inside properties.
Error check outside class in another class, and then just add the verified inputs to Details
Use a boolean method inside Details to verify the inputs.
I would follow an existing validation framework, such as FluentValidation.
Also, in your specific case, I would have a SetName(string fullName) method that does the parsing and populating.
Why not use exception to capture the error condition.
private string firstName, lastName;
public string Name
{
get { return string.Concat(firstName, " ", lastName); }
set
{
string name = value;
if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("Name"); }
var nameParts = name.Trim().Split(' ');
if (nameParts.Length != 2) { throw new ArgumentException("Invalid name value"); }
firstName = nameParts[0];
lastName = nameParts[1];
}
}
Just to add my perspective to what others have said, it seems to me that part of the difficulty stems from the fact that you're using a property setter to do something non-trivial (split a string, validate the results, and then store the results in two fields). Generally, properties, especially read/write properties, should only be concerned with getting and setting a single value (using read-only properties to return a simple computed value is common enough too).
If it were me, I'd have separate properties for the first name and last name, with a third computed property for the full name:
class Details
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get { return string.Concat(this.FirstName, " ", this.LastName); }
}
}
Then you could add a method to set the full name. A method is more appropriate than a property setter for this task, since it's non-trivial and has more potential for problems:
public void SetFullName(string fullName)
{
string[] nameComponents = fullName.split(' ');
if (nameComponents.Length != 2)
{
throw new ArgumentException("The full name must contain a first and last name.");
}
this.FirstName = nameComponents[0];
this.LastName = nameComponents[1];
}
I also want to give a plug for the Code Contracts package. It may be more complication than you're looking for here, but it's a great way of validating input and output in your applications.
So I wouldn't consider data that doesn't meet business logic an exception and thus wouldn't throw one. What I would do is this:
class Details
{
private string firstName, lastName;
public string Name
{
// name
get { return firstName + " " + lastName; }
set
{
string name = value;
string[] nameArray = name.Split(' ');
if(nameArray.Length == 2)
{
firstName = nameArray[0];
lastName = nameArray[1];
}
else
{
firstName = nameArray[0];
lastName = string.Empty;
}
}
}
public bool IsValid()
{
return !string.IsNullOrEmpty(lastName);
}
}
You can then use the name property and then check to see if the name is valid. If not valid, then you can take the appropriate action.
Another option would be to have the validation done in the method calling Details.Name.
EDIT: want to remove what I think is bad advice but keeping so comments make sense, so just striking them out
EDIT2:
You could also do something like this:
class Details
{
private string firstName, lastName;
public string Name
{
get { return firstName + " " + lastName; }
private set;
}
public bool TryParseName(string name)
{
bool isValid = true;
string[] nameParts = name.split(' ');
if(nameParts.Length == 2)
{
firstName = nameParts[0];
lastName = nameParts[1];
}
else
{
isValid = false;
}
return isValid;
}
}
Where you would do
if(details.TryParseName(name))
{
// is valid name
}
else
{
// handle invalid name
}
I have a static class in my solution that is basically use a helper/ultility class.
In it I have the following static method:
// Set the user
public static void SetUser(string FirstName, string LastName)
{
User NewUser = new User { Name = String.Format("{0}{1}", FirstName, LastName) };
HttpCookie UserName = new HttpCookie("PressureName") { Value = NewUser.Name, Expires = DateTime.Now.AddMinutes(60) };
}
User is a simple class that contains:
String _name = string.Empty;
public String Name
{
get { return _name; }
set { _name = value; }
}
Everything works up until the point where I try to write the cookie "PressureName" and insert the value in it from NewUser.Name. From stepping through the code it appears that the cookie is never being written.
Am I making an obvious mistake? I'm still very amateur at c# and any help would be greatly appreciated.
Creating the cookie object is not enough to have it sent to the browser. You have to add it to the Response object also.
As you are in a static method, you don't have direct access to the page context and it's Response property. Use the Current property to access the Context of the current page from the static method:
HttpContext.Current.Response.Cookies.Add(UserName);