Writing to file using properties - c#

I'm trying to write to a file at the moment I create my HighScores object. I'm trying to use the Name and Score properties as the text to the file, but they seem to be null and 0 respectively even though I initialized the object. So my question is why is it not writing "David : 88 "?
static void Main(string[] args)
{
HighScores David = new HighScores() { Name = "David", Score = 88 };
}
class HighScores
{
public string Name { get; set; }
private int score;
public int Score
{
get
{
if (score < 50)
{
return 0;
}
return score;
}
set
{
score = value;
}
}
public HighScores()
{
// Opening and writing to the file
FileStream fileStream = File.OpenWrite(path);
StreamWriter writer = new StreamWriter(fileStream);
writer.Write($"{Name} : {Score} \n");
writer.Close();
}
}

I think the issue is that the Constructor runs before any of the "sets" in your code. Setting breakpoints in your code (in your constructor, in the property sets) and using Step Into, might help see what order all the code is being run in.
So, instead of writing the values in the constructor, refactor that into an actual method.
change the line
public HighScores()
to
public void SaveScores()
then add the line after you "new" up your object.
David.SaveScores();
That should work.
I'd also look into leveraging the using/Dispose pattern as well.
using (var fileStream = File.OpenWrite(path))
{
// do stuff
}
// dotNet will be sure to call Dispose and clean up the fileStream.

As Andre correctly points out, the “Constructor” public HighScores() is called when you create a new HighScores object like you have below.
HighScores David = new HighScores() { Name = "David", Score = 88 };
Unfortunately the properties Name and Score have not been initialized. Since it is a “Constructor” simply pass the variables as you would a normal constructor like below:
HighScores David = new HighScores("David", 88);
Then set the matching signature in the HighScores “Constructor” then you can set the properties and it should work as expected, however I agree with Andre as this (writing to file) should be a separate method and NOT part of the “Constructor” Hope that makes sense.
public HighScores(string name, int score) {
Name = name;
Score = score;
using (FileStream fileStream = File.OpenWrite(path)) {
StreamWriter writer = new StreamWriter(fileStream);
writer.Write($"{Name} : {Score} \n");
writer.Close();
}
}
.

Related

Why Can't I Populate an Array inside a method declaration in C#?

I am writing an application where I need to read a set of JSON files and create objects from a model in my application. This doesn't seem all that difficult and the code looks right to me, but I am using an array to store the JSON strings, and for some reason Visual Studio is red underlining the array name and saying it's an "unassigned local variable", even though I declare it before the foreach loop.
I'm somewhat of a novice in C#, so if someone could let me know how to correct this I would greatly appreciate it.
The line in question starts with "lotRanges[i] = JsonConvert..."
namespace InternalReceiptImport.Services
{
interface ILotRangeService
{
List<LotRange> GetAll();
}
public class LotRangeService : ILotRangeService
{
public List<LotRange> GetAll()
{
string jsonFilePath = #"\Data";
Array files = Directory.GetFiles(jsonFilePath);
LotRange[] lotRanges;
int i = 0;
foreach (string filename in files)
{
string filepath = jsonFilePath + "\\" + filename;
string json = File.ReadAllText(filepath);
lotRanges[i] = JsonConvert.DeserializeObject<LotRange>(json);
i++;
}
List<LotRange> listLotRanges = lotRanges.ToList();
return listLotRanges;
}
}
}
It was suggested below I just use a list instead of an array. I tried that, but it's giving me the same error on the line I am using to add to the list. Here is the code...
namespace InternalReceiptImport.Services
{
interface ILotRangeService
{
List<LotRange> GetAll();
}
public class LotRangeService : ILotRangeService
{
public List<LotRange> GetAll()
{
string jsonFilePath = #"\Data";
Array files = Directory.GetFiles(jsonFilePath);
List<LotRange> listLotRanges;
int i = 0;
foreach (string filename in files)
{
string filepath = jsonFilePath + "\\" + filename;
string json = File.ReadAllText(filepath);
listLotRanges.Add(JsonConvert.DeserializeObject<LotRange>(json));
i++;
}
return listLotRanges;
}
}
}
in both your examples the problem is that lotRanges is declared but it has not been assigned a value yet, that is, it is null. In order to fix this, all you have to do is assign a value to your declared variable. In the array case, you have to define the size of it upfront:
Array files = Directory.GetFiles(jsonFilePath);
LotRange[] lotRanges = new LotRange[files.Length];
And in the case of using a List<LotRange> you don't need to know the size upfront, which is one of the reasons why people tend to prefer using List<T> for scenarios like this.
List<LotRange> lotRanges = new List<LotRange>();

Ban a variable from a list with a "ban" list

How can I ban a variable from a list without removing it from that list by adding the variable to a list of "banned" variable?
I wish to be able to type in a string. That string is compared to the file names in a folder. If there is a match, the file is read. If I type this same string again, the file should not be read again. There for I want to have a list of "banned" string that is checked whilst typing to avoid the file to be read again.
I have tried a few ways but not getting there. Below is an example of my last attempt.
What would be the best way?
public class test
{
string scl= "test3";
List <string> lsf,lso;
void Start ()
{
lsf=//file names
new List<string>();
lso=//files open
new List<string>();
lsf.Add("test0");
lsf.Add("test1");
lsf.Add("test2");
lsf.Add("test3");
lsf.Add("test4");
lso.Add("idhtk49fngo");//random string
}
void Update ()
{
if
(
Input.GetKeyDown("a")
)
{
for
(
int i=0;
i<lsf.Count;
i++
)
{
if(lsf[i]==scl)
{
Debug.Log
(i+" is read");
for
(
int j=0;
j<lso.Count;
j++
)
{
//how can i avoid reading
//lsf[3] here the second time
//"a" is pressed (by having "test3"
//added to a "ban" list (lso) )
if(scl!=lso[j])
{
lso.Add(lsf[i]);
}
}
}
}
}
}
Michael’s answer is the way to go here but it can be improved using the more appropriate collection available to keep track of opened files; if you want uniqueness use a set, not a list:
HashSet<string> openedFiles = new HashSet<string>();
public static bool TryFirstRead(
string path,
out string result)
{
if (openedFiles.Add(path))
{
result = File.ReadAllText(path);
return true;
}
result = null;
return false;
}
Also, I’d avoid throwing vexing exceptions. Give the consumer a friendly way to know if the file was read or not, don’t make them end up having to use exceptions as a flow control mechanism.
I didn't understand although if you want to replace a value from another list.
You can use the list index to create a new list with the values which you removed.
String list1 = {"hi", "hello", "World"};
String list2 = {"bye", "goodbye", "World"};
List1[1] = list2[1];
I would suggest such way:
public static List<string> openedFiles = new List<string>();
public static string ReadFileAndAddToOpenedList(string path)
{
if (openedFiles.Contains(path))
throw new Exception("File already opened");
// Instead of throwing exception you could for example just log this or do something else, like:
// Consolle.WriteLine("File already opened");
else
{
openedFiles.Add(path);
return File.ReadAllText(path);
}
}
The idea is - on every file read, add file to list, so you can check every time you try read file, if it was already read (or opened). If it is, throw exception (or do something else). Else read a file.
You could instead of making it a string list use your own class
public class MyFile
{
public string Name;
public bool isOpen;
public MyFile(string name)
{
Name = name;
isOpen = false;
}
}
List<MyFile> lsf = new List<MyFile>()
{
new MyFile("test0"),
new MyFile("test1"),
new MyFile("test2"),
new MyFile("test3"),
new MyFile("test4")
};
Than when you read the file set isOpen to true
MyFile[someIndex].isOpen = true;
and later you can check this
// E.g. skip in a loop
if(MyFile[someIndex]) continue;
You could than also use Linq in order to get a list of only unread files:
var unreadFiles = lsf.Select(f => f.Name).Where(file => !file.isOpen);

Creating and storing values of class objects from a text file

I am trying to find a way to creat class instances from a file and also to use that file as a way to give the class properties their values. I can manually put all the information in but it will be better to do it through a file so I can alter the file, which will alter the program for me.
Here is the code so far... When I run it, it says
class Program
{
class dish
{
public class starters { public string starter; public string alteration; }
static void Main(string[] args)
{
List<dish.starters> starter = new List<dish.starters>();
using (StreamReader reader = File.OpenText(#"D:\Visual Studio\Projects\Bella Italia\Food\Starters.txt"))
{
IDictionary<string, dish.starters> value = new Dictionary<string, dish.starters>();
while (!reader.EndOfStream)
{
value[reader.ReadLine()] = new dish.starters();
value[reader.ReadLine()].starter = reader.ReadLine();
}
foreach(var x in value.Values)
{
Console.WriteLine(x.starter);
}
}
Console.ReadLine();
}
}
}
When I try to run it, it says
Exception Unhandled
System.Collections.Generic.KeyNotFoundException: 'The given key was not present in the dictionary.'
You are reading two consecutive line here. The second line probably doesn't have an associated entry in the dictionary (and you don't want that duplication either):
value[reader.ReadLine() /*one*/] = new dish.starters();
value[reader.ReadLine() /*two*/].starter = reader.ReadLine();
Store the key in a variable and reuse that:
string key = reader.ReadLine();
value[key] = new dish.starters();
value[key].starter = reader.ReadLine();
Or create the object and assign later:
string key = reader.ReadLine();
var starters = new dish.starters();
starters.starter = reader.ReadLine()
value[key] = starters;

Files,strings and save

I been having trouble trying to figure this out. When I think I have it I get told no. Here is a picture of it.
I am working on the save button. Now after the user adds the first name, last name and job title they can save it. If a user loads the file and it comes up in the listbox, that person should be able to click on the name and then hit the edit button and they should be able to edit it. I have code, but I did get inform it looked wackey and the string should have the first name, last name and job title.
It is getting me really confused as I am learning C#. I know how to use savefiledialog but I am not allowed to use it on this one. Here is what I am suppose to be doing:
When the user clicks the “Save” button, write the selected record to
the file specified in txtFilePath (absolute path not relative) without
truncating the values currently inside.
I am still working on my code since I got told that it will be better file writes records in a group of three strings. But this is the code I have right now.
private void Save_Click(object sender, EventArgs e)
{
string path = txtFilePath.Text;
if (File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
foreach (Employee employee in employeeList.Items)
sw.WriteLine(employee);
}
}
else
try
{
StreamWriter sw = File.AppendText(path);
foreach (var item in employeeList.Items)
sw.WriteLine(item.ToString());
}
catch
{
MessageBox.Show("Please enter something in");
}
Now I can not use save or open file dialog. The user should be able to open any file on the C,E,F drive or where it is. I was also told it should be obj.Also the program should handle and exceptions that arise.
I know this might be a noobie question but my mind is stuck as I am still learning how to code with C#. Now I have been searching and reading. But I am not finding something to help me understand how to have all this into 1 code. If someone might be able to help or even point to a better web site I would appreciate it.
There are many, many ways to store data in a file. This code demonstrates 4 methods that are pretty easy to use. But the point is that you should probably be splitting up your data into separate pieces rather than storing them as one long string.
public class MyPublicData
{
public int id;
public string value;
}
[Serializable()]
class MyEncapsulatedData
{
private DateTime created;
private int length;
public MyEncapsulatedData(int length)
{
created = DateTime.Now;
this.length = length;
}
public DateTime ExpirationDate
{
get { return created.AddDays(length); }
}
}
class Program
{
static void Main(string[] args)
{
string testpath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TestFile");
// Method 1: Automatic XML serialization
// Requires that the type being serialized and all its serializable members are public
System.Xml.Serialization.XmlSerializer xs =
new System.Xml.Serialization.XmlSerializer(typeof(MyPublicData));
MyPublicData o1 = new MyPublicData() {id = 3141, value = "a test object"};
MyEncapsulatedData o2 = new MyEncapsulatedData(7);
using (System.IO.StreamWriter w = new System.IO.StreamWriter(testpath + ".xml"))
{
xs.Serialize(w, o1);
}
// Method 2: Manual XML serialization
System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(testpath + "1.xml");
xw.WriteStartElement("MyPublicData");
xw.WriteStartAttribute("id");
xw.WriteValue(o1.id);
xw.WriteEndAttribute();
xw.WriteAttributeString("value", o1.value);
xw.WriteEndElement();
xw.Close();
// Method 3: Automatic binary serialization
// Requires that the type being serialized be marked with the "Serializable" attribute
using (System.IO.FileStream f = new System.IO.FileStream(testpath + ".bin", System.IO.FileMode.Create))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bf.Serialize(f, o2);
}
// Demonstrate how automatic binary deserialization works
// and prove that it handles objects with private members
using (System.IO.FileStream f = new System.IO.FileStream(testpath + ".bin", System.IO.FileMode.Open))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
MyEncapsulatedData o3 = (MyEncapsulatedData)bf.Deserialize(f);
Console.WriteLine(o3.ExpirationDate.ToString());
}
// Method 4: Manual binary serialization
using (System.IO.FileStream f = new System.IO.FileStream(testpath + "1.bin", System.IO.FileMode.Create))
{
using (System.IO.BinaryWriter w = new System.IO.BinaryWriter(f))
{
w.Write(o1.id);
w.Write(o1.value);
}
}
// Demonstrate how manual binary deserialization works
using (System.IO.FileStream f = new System.IO.FileStream(testpath + "1.bin", System.IO.FileMode.Open))
{
using (System.IO.BinaryReader r = new System.IO.BinaryReader(f))
{
MyPublicData o4 = new MyPublicData() { id = r.ReadInt32(), value = r.ReadString() };
Console.WriteLine("{0}: {1}", o4.id, o4.value);
}
}
}
}
As you are writing the employee objects with WriteLine, the underlying ToString() is being invoked. What you have to do first is to customize that ToString() methods to fit your needs, in this way:
public class Employee
{
public string FirstName;
public string LastName;
public string JobTitle;
// all other declarations here
...........
// Override ToString()
public override string ToString()
{
return string.Format("'{0}', '{1}', '{2}'", this.FirstName, this.LastName, this.JobTitle);
}
}
This way, your writing code still keeps clean and readable.
By the way, there is not a reverse equivalent of ToSTring, but to follow .Net standards, I suggest you to implement an Employee's method like:
public static Employee Parse(string)
{
// your code here, return a new Employee object
}
You have to determine a way of saving that suits your needs. A simple way to store this info could be CSV:
"Firstname1","Lastname 1", "Jobtitle1"
" Firstname2", "Lastname2","Jobtitle2 "
As you can see, data won't be truncated, since the delimiter " is used to determine string boundaries.
As shown in this question, using CsvHelper might be an option. But given this is homework and the constraints therein, you might have to create this method yourself. You could put this in Employee (or make it override ToString()) that does something along those lines:
public String GetAsCSV(String firstName, String lastName, String jobTitle)
{
return String.Format("\"{0}\",\"{1}\",\"{2}\"", firstName, lastName, jobTitle);
}
I'll leave the way how to read the data back in as an exercise to you. ;-)

C# Read in multiple profiles from streamreader and give outputs

I have been working on a program that goes along with a game me and some of my friends play, all I have done so far is make the interface, figured I would ask for help in advance because I know ill need it. I wanted to make a GUI that would read in data of a character from a text file, fill text boxes with it and be able to do equations with two of the boxes, then move onto another character from that text file by hitting next.
sample read in data would be :
Interface
Hark <- new character
5
2
6
40.0
12.00
Caro <- new character
6
1
8
38.0
10.00
Ethan <- new character
4
5
42.0
15.00
(The above is not actually code, just how it posted)
So "name" through "char num" would just be populated by data.Readline(); from streamreader, but HP and armor would need to be times-ed together to get ratio (hitting the compute ratio button), all data from name to ratio would go in the text box below the buttons, the next button would cycle to the next character.
Any help would be much appreciated, thanks in advance.
It's going to be a lot easier to do it with XML serialization/deserialization.
Here's a complete demo. It creates a list of two characters, serializes it to XML, then deserializes it back into a new List. This will take care of the storage aspect of it, at least.
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace XmlDemo
{
public class CharacterAttributes
{
public string Name { get; set; }
public int Strength { get; set; }
public int Dexterity { get; set; }
}
class Program
{
static void Main(string[] args)
{
var characters = new List<CharacterAttributes>
{
new CharacterAttributes
{
Name = "Throgdor the Destroyer",
Strength = 5,
Dexterity = 10
},
new CharacterAttributes
{
Name = "Captain Awesome",
Strength = 100,
Dexterity = 9
}
};
SerializeToXML(characters);
var charactersReloaded = DeserializeFromXML(#"C:\temp\characters.xml");
}
static public void SerializeToXML(List<CharacterAttributes> characters)
{
var serializer = new XmlSerializer(typeof(List<CharacterAttributes>));
var textWriter = new StreamWriter(#"C:\temp\characters.xml");
using (textWriter)
{
serializer.Serialize(textWriter, characters);
textWriter.Close();
}
}
public static List<CharacterAttributes> DeserializeFromXML(string path)
{
var serializer = new XmlSerializer(typeof(List<CharacterAttributes>));
var textReader = new StreamReader(path);
var deserializedCharacters = new List<CharacterAttributes>();
using (textReader)
{
deserializedCharacters = serializer.Deserialize(textReader) as List<CharacterAttributes>;
}
return deserializedCharacters;
}
}
}
I would probably do a File.ReadAllText ( or something like that) into a string. That way you release the file handle. Then you can loop over the characters in the string that you read from the file and do whatever you want.

Categories

Resources