Calling a method that expects an array of objects - c#

I'm learning C# and have written a console program to save an an array of high scores to a file. Although the program works, how I have got it to work is making me feel uneasy and feels more of a hack than a solution so I was looking for guidance on how I should have written it.
What I am currently doing within the Main method is:
Declaring an array of highscore objects
Initialising them
Assigning some values to the array.
I am happy with what I have done up until now, it's the following two steps that make me uneasy
I then declare another HighScore object
I use this object to pass the array of highscores to the SaveHighScores method.
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace HighScore
{
class HighScore
{
public string Name { get; set; }
public int Score { get; set; }
public void SaveHighScores(HighScore[] highScores)
{
string allHighScoresText = "";
foreach (HighScore score in highScores)
{
allHighScoresText += $"{score.Name},{score.Score}" + Environment.NewLine;
}
File.WriteAllText("C:/Temp/highscores.csv", allHighScoresText);
}
static void Main(string[] args)
{
HighScore[] highScore = new HighScore[2];
for (int i = 0; i < highScore.Length; i++)
{
highScore[i] = new HighScore();
}
highScore[0].Name = "A";
highScore[0].Score = 100;
highScore[1].Name = "B";
highScore[1].Score = 200;
// are the following two lines correct or am I missing something?
HighScore hs = new HighScore();
hs.SaveHighScores(highScore);
}
}
}

Make SaveHighScores static and you won't need an instance of HighScore to call it. (You can call it directly as HighScore.SaveHighScores())

I prefer to split the representation of your data from the actions that you perform on this data. So I would go for two classes, one for the Data and one for the Save/Load and other business logic
public class HighScore
{
public string Name { get; set; }
public int Score { get; set; }
}
// This class handles the core work to persist your data on the storage medium
// The class is static so you don't need to declare instances and use directly the methods available.
public static class Repo_HighScore
{
// For simplicity, no error Handling but, for a robust implementation,
// error handling is required
public static bool SaveHighScores(HighScore[] highScores)
{
StringBuilder allHighScoresText = new StringBuilder();
foreach (HighScore score in highScores)
allHighScoresText.AppendLine($"{score.Name},{score.Score}");
File.WriteAllText("C:/Temp/highscores.csv", allHighScoresText.ToString());
}
public static HighScore[] LoadHighScores()
{
List<HighScore> hs = new List<HighScore>();
foreach(string line in File.ReadLines("C:/Temp/highscores.csv"))
{
string[] parts = line.Split(',');
HighScore temp = new HighScore()
{ Name = parts[0], Score = Convert.ToInt32(parts[1])};
hs.Add(temp);
}
return hs.ToArray();
}
}

Related

C# How to add string to a list from a different file

I'm new to C# and OOP I have two different files. File A where I've created the list.
//This file will contain predetermine list of responses.
using System.Linq;
using System.Collections.Generic;
public class Responses
{
//bot name
static string nomber = "Jarvis";
List<string> answer = new List<string>(){
$"Mi nomber ta {nomber}",
"Mi ta bon"
};
public void AddToList(string value){
this.answer.Add(value);
}
public string Answer(int id)
{
return answer.ElementAt(id);
}
}
And in file B I have these two lines of code to add the string 1 to the list, I've also included the Generics and Linq System in file B.
var response = new Responses();
response.answer.Add("1");
I've tried creating a method called AddToList to pass the value and add it to the list, but with no luck. When I try to display the list at index 2 I'll get an argument out of range instead of the value "1".
*Also both files are located in the same folder.
After read your source code, I understand your problem. First you Add new element to Response.answers of Interperter and it return id then you get Response.answer of Output by that id. Of course you never get that, because they were 2 different instances.
I provide 2 options for you:
Option 1: Make Reponses single instance (singleton)
Responses.cs
using System.Linq;
using System.Collections.Generic;
public class Responses
{
private static Responses _instance = new Responses();
public static GetInstance() {
return _instance;
}
//bot name
static string nomber = "Jarvis";
List<string> answer = new List<string>(){
$"Mi nomber ta {nomber}",
"Mi ta bon"
};
public void AddToList(string value){
this.answer.Add(value);
}
public string Answer(int id)
{
return answer.ElementAt(id);
}
}
Then change on other files
//from
var responses = new Responses();
//to
var responses = Responses.GetInstance();
//from
responses.answer.Add()
//to
reponses.AddToList()
Option 2: Make Responses static
Response.cs
using System.Linq;
using System.Collections.Generic;
public static class Responses
{
//bot name
static string nomber = "Jarvis";
static List<string> answer = new List<string>(){
$"Mi nomber ta {nomber}",
"Mi ta bon"
};
public static void AddToList(string value){
this.answer.Add(value);
}
public static string Answer(int id)
{
return answer.ElementAt(id);
}
}
Output.cs
using System;
public class Output
{
public void Return(int respondType, int respond)
{
switch(respondType)
{
case 0:
Console.WriteLine(Responses.Answer(respond));
break;
default:
Console.WriteLine("Mi no ta kompronde");
break;
}
}
}
Interperter.cs
using System.Collections.Generic;
using System.Linq;
public class Interpreter
{
public int UserInputType(string value)
{
// Turns the user input into an array of words
string[] words = value.Split(' ');
int returnValue = 2;
//int match = 0;
Responses.AddToList("1");
//This stores the correct response to the given question
//var element = new List<int>();
foreach(var word in words)
{
// if(!string.IsNullOrWhiteSpace(word))
// {
foreach(var listOfQuestions in userInputedQuestions)
{
//Convert words in the listOfQuestions to array string to match them with the userInputedQuestion
string[] listOfQWords = listOfQuestions.Split(" ");
//Check how many words matches the predefined list of questions
foreach(var qWord in listOfQWords){
if(word == qWord){
returnValue = 0;
}
}
}
}
// }
return returnValue;
}
private List<string> userInputedQuestions = new List<string>(){
"Ki ta bo nomber?",
"Konta ku bo?"
};
}
Hope it helps

How do I mock a text output from reading a file in C# using the Moq Framework on Monodevelop

I've been banging my head on this all weekend. Basically I am doing a code kata for Game of Life and it involves reading in a text file. I take in that text file which contains two dimensional representation of the grid and stores all the points in a List of List's. I am trying to mock the text input obtained from the file to just be '\n' a new line so I can write unit tests checking that there is a new List being created within the List of Lists. I have created a file wrapper to handle the reading of the text file and that is what I am trying to mock. The code complies fine but the test fails with the error message "System.ArgumentException : The specified path is not of a legal form". It seems to still be expecting a file path but the mocking should change this behaviour right? Any help would be appreciated.
using System.Collections.Generic;
namespace GameOfLife
{
public class InitializeGrid
{
public InitializeGrid ()
{
}
public List<List<char>> CreateCharMatrix (string filePathName)
{
// Reads in the text file containing the grid data
FileWrapper fileWrapper = new FileWrapper ();
string inputGridTextFile = fileWrapper.ReadTextFromFile (filePathName);
// Creates character matrix and initialises the first sub List
List<List<char>> charMatrix = new List<List<char>> ();
charMatrix.Add(new List<char>());
int rowIndex = 0;
int colIndex = 0;
foreach (char cell in inputGridTextFile) {
if (cell == '\n') {
charMatrix.Add (new List<char> ());
rowIndex++;
colIndex = 0;
} else {
charMatrix [rowIndex] [colIndex] = cell;
colIndex++;
}
}
return charMatrix;
}
}
}
using NUnit.Framework;
using System;
using System.Collections.Generic;
using Moq;
namespace GameOfLife
[TestFixture()]
public class InitializeGridTest
{
[Test()]
public void CreateCharMatrix_EnsuresThatWhenEndOfLineReachedNewSubListCreated()
{
//Arrange
InitializeGrid initializeGrid = new InitializeGrid ();
List<List<char>> charMatrix;
string filePathName = " ";
Mock<IFileWrapper> mockFileWrapper = new Mock<IFileWrapper> ();
mockFileWrapper.Setup<string> (m => m.ReadTextFromFile (It.IsAny<string>())).Returns ("\n");
mockFileWrapper.Setup (m => m.ReadTextFromFile (It.IsAny<string>())).Returns ("\n");
//Act
charMatrix = initializeGrid.CreateCharMatrix (filePathName);
int countProvingAnAdditionalListHasBeenAdded = charMatrix.Count;
//Assert
Assert.AreEqual (2, countProvingAnAdditionalListHasBeenAdded);
}
}
using System;
using System.IO;
namespace GameOfLife
{
public class FileWrapper : IFileWrapper
{
public string ReadTextFromFile(string path)
{
return File.ReadAllText (path);
}
}
}
using System;
namespace GameOfLife
{
public interface IFileWrapper
{
string ReadTextFromFile(string filePathName);
}
}
Looking at your code the InitializeGrid is still using the FileWrapper class. It is not using a mocked class so the code is still trying to use the file system.
Your InitializeGrid class needs to use the IFileWrapper interface and not the FileWrapper class. I would look at passing the IFileWrapper interface into the constructor of the InitializeGrid class.
public class InitializeGrid
{
IFileWrapper fileWrapper;
public InitializeGrid (IFileWrapper fileWrapper)
{
this.fileWrapper = fileWrapper;
}
public List<List<char>> CreateCharMatrix (string filePathName)
{
string inputGridTextFile = fileWrapper.ReadTextFromFile (filePathName);
// More code here...
}
}
In your test you would construct the InitializeGrid object using the mocked IFileWrapper by passing the mockFileWrapper.Object to its constructor.
List<List<char>> charMatrix;
string filePathName = " ";
Mock<IFileWrapper> mockFileWrapper = new Mock<IFileWrapper> ();
mockFileWrapper.Setup<string> (m => m.ReadTextFromFile (It.IsAny<string>())).Returns ("\n");
mockFileWrapper.Setup (m => m.ReadTextFromFile (It.IsAny<string>())).Returns ("\n");
InitializeGrid initializeGrid = new InitializeGrid (mockFileWrapper.Object);

How to make a multidimensional Hashtable

I have this data and I need to make an object out of it so I can easily read/write to this object.
I tried to make a dictionary and fill it with hashtables but it is very hard to maintain and I don't even know how to modify the data inside the hashtable inside that dictionary.
this is my bad practice:
Dictionary<string,Hashtable> DICTFormsControls = new Dictionary<string,Hashtable>();
DICTFormsControls[parentfrm] = new Hashtable();
DICTFormsControls[parentfrm][controlid] = new FormControl(controlnm,parentfrm,controlid,controlpermission);
offcourse the "FormControl" is just a simple class to hold the control proberties.
I need to make it so I can get the data easily and modify it just as easy.
A dictionary already contains a hash table for the key. Try something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DICTFormsControls dictFormcontrols = new DICTFormsControls();
dictFormcontrols.Add(controlnm,parentfrm,controlid,controlpermission)
}
}
public class DICTFormsControls
{
public static Dictionary<int, DICTFormsControls> dict = new Dictionary<int, DICTFormsControls>();
public string controlnm { get; set;}
public string parentfrm { get; set;}
public int controlid { get; set;}
public bool controlpermission {get;set;}
public void Add(string controln, string parentfrm, int controlid, bool controlpermission)
{
dict.Add(controlid, new DICTFormsControls() { controlnm = controlnm, parentfrm = parentfrm, controlid = controlid, controlpermission = controlpermission });
}
}
}
​

XML serialize without overwriting

How can I serialize data to xml without overwriting previous data?
In this code I am able to make XML file but when I run this again I overwrite what I saved previously:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Xml.Serialization;
namespace ActorGenerator
{
class Program
{
static void Main()
{
Console.Write("How many actors you want to add? ");
int max = 0;
max = int.Parse(Console.ReadLine());
for (int i = 0; i < max; i++)
{
int x = i + 1;
Console.WriteLine("////////////////////////ACTOR" + x + "/////////////////////////////////");
Actor actor1 = new Actor();
Console.Write("NAME (string): "); actor1.Name = Console.ReadLine();
Console.Write("AGE (int): "); actor1.Age = int.Parse(Console.ReadLine());
Console.Write("FOCUS (string): "); actor1.Focus = Console.ReadLine();
Console.Write("PRICE (int): "); actor1.Price = int.Parse(Console.ReadLine());
Console.Write("CONTRACTED (true/false): "); actor1.Contracted = bool.Parse(Console.ReadLine());
Console.Write("PLAYING IN FILM (true/false): "); actor1.PlayingInFilm = bool.Parse(Console.ReadLine());
Console.Write("SKILL (int): "); actor1.Skill = int.Parse(Console.ReadLine());
SerializeToXML(actor1);
}
}
static public void SerializeToXML(Actor actor)
{
XmlSerializer serializer = new XmlSerializer(typeof(Actor));
TextWriter textWriter = new StreamWriter(#"c:\users\Desktop\actor.xml");
serializer.Serialize(textWriter, actor);
textWriter.Close();
}
}
public class Actor
{
public string Name { get; set; }
public int Age { get; set; }
public string Focus { get; set; }
public int Price { get; set; }
public bool Contracted { get; set; }
public bool PlayingInFilm { get; set; }
public int Skill { get; set; }
}
}
Also, how can I read XML data and store it into variable?
It sounds like you should create a List<Actor> - you can then start with a list of one entry, serialize that list, then next time round deserialize the list, add an actor, and then serialize again so you'll have two entries in the list the following time, etc.
To read the data, load the file to a StreamReader, and then use the Deserialize method on the serializer:
XmlSerializer serializer = new XmlSerializer(typeof(Actor));
TextReader textReader = new StreamReader(#"c:\users\Desktop\actor.xml");
Actor actor = (Actor)serializer.Deserialize(textReader);
textReader.Close();
(Assuming you only have one actor you're saving in the XML file)
To not overwrite, I recommend you read the data, as described above, and then update the object, and then finally write to the XML file. This way you preserve what was originally in the XML file as desired.
(Assuming you want many actors in the XML file)
You likely want to serialize a List rather than just Actor. If so, you can still read the data in (cast to List instead), update the list as necessary, then serialize back out to the XML file.
PS: Instead of using "Close" on your readers/writers, and other IDisposable objects, I recommend you use the "using" statement in C#.
Just pass a true into your constructor for the StreamWriter
static public void SerializeToXML(Actor actor)
{
XmlSerializer serializer = new XmlSerializer(typeof(Actor));
TextWriter textWriter = new StreamWriter(#"c:\users\Desktop\actor.xml",true);
serializer.Serialize(textWriter, actor);
textWriter.Close();
}
As far as fixing the Xml, you could use Linq To XML http://blogs.msdn.com/b/wriju/archive/2008/02/18/linq-to-xml-creating-complex-xml-through-linq.aspx to create valid XML and save it to file in one shot

C# Foreach Loop Issue

I was just making this program to experiment with lists and such, and I was curious as to why in the foreach loop the Object always shows up as the "Minecraft" Wish object. Is it because it was the last Wish object to be created? And how can I fix it, so all 3 Wish objects which have been declared show up?
Thanks!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Wish iPod = new Wish("iPod", "Various", 299.00);
Wish Phone = new Wish("New Phone", "Various", 00.00);
Wish Minecraft = new Wish("Minecraft Account", "www.minecraft.net", 30.00);
List<Wish> Wishlist = new List<Wish>();
Wishlist.Add(Phone);
Wishlist.Add(iPod);
Wishlist.Add(Minecraft);
Console.WriteLine("Toby's Wishlist");
Console.WriteLine("If cost is 00.00, the Wish's cost varies.");
Console.WriteLine(" ");
foreach (Wish wish in Wishlist)
{
Console.WriteLine("---Wish---");
Console.WriteLine("Name: {0}", wish.getName());
Console.WriteLine("Store: {0}", wish.getStore());
Console.WriteLine("Cost: ${0}", wish.getCost().ToString());
Console.WriteLine("----------");
Console.WriteLine(" ");
}
Console.ReadLine();
}
}
public class Wish
{
static string Name, Store;
static double ApproxCost;
public Wish(string name, string store, double approxCost)
{
Name = name;
Store = store;
ApproxCost = approxCost;
}
public string getName()
{
return Name;
}
public string getStore()
{
return Store;
}
public double getCost()
{
return ApproxCost;
}
}
}
Remove static from Wish members declaration
static means that the data will be shared across all the instances. So static members are also so called class variables. While not static members - are object variables.
It's because in class Wish you declared Name, Score, and ApproxCost as static.

Categories