Output elements from a struct array - c#

I'm tasked to ask the user for their data (name, last name, email & password), but I'm constrained to only using 25 lines per method which means that I have to use MANY of them. I'm using a struct for the data, and vectors for each user. However, when I want to display the users information it shows completely blank or just the direction of the array. I've tried many things with little to no success.
Here's the code in question.
using System;
public struct Usuario
{
public String name;
public String lastname;
public String email;
public String phone;
public String password;
}
static void Main()
{
Console.Clear();
Usuario[] user = new Usuario[5];
Array.Copy(user, UserNew(), 5);
UserView(user);
}
static Usuario[] UserNew()
{
int control = 1, i = 0;
Console.Clear();
Usuario[] register = new Usuario[5]; //It's supposed to have 5 users
Console.Write("\t\t¡Bienvenido al registro de usuario!\n\n");
while(control == 1)
{
register[i].name = Name();
register[i].lastname = LastName();
register[i].email = Email();
register[i].phone = Phone();
register[i].password = Psswrd();
Console.Write("\n\n\t¿Desea registrar otro usario?\n\tDigite 1 para registrar otro: ");
control = int.Parse(Console.ReadLine());
i++;
}
Menu();
return register;
}
static void UserView(Usuario[] UserReg)
{
UserReg = new Usuario();
Console.Write("\n\tIntroduzca el código de acceso: ");
String access = Asterisk('*');
if (access == "123456")
{
for (int i = 0; i <= 2; i++)
{
Console.Write("\n" + UserReg[i]);
}
}
else
{
Console.Write("\n\n\tEl código de acceso es incorrecto!");
Thread.Sleep(2000);
Menu();
}
}

This is a perfect example of why you should ALWAYS read the relevant documentation, especially when something doesn't work as you expect. If you had debugged your code then you would have seen that UserNew returns an array as expected but, after calling Array.Copy, it's contents is not copied to the user array. If you had then consulted the documentation for the Array.Copy method then you would have seen that the first parameter is the source and the second is the destination. You're copying the empty array over the full one, instead of the other way around. If you pass the source and destination arrays in the correct order, things will work as expected:
Array.Copy(UserNew(), user, 5);
That said, the way you're doing things doesn't really make much sense. You start by creating an array with a number of empty items, then you create another array with populated items, then you copy the data from the populated array to the empty one. Why not just use the populated array? This:
Usuario[] user = new Usuario[5];
Array.Copy(user, UserNew(), 5);
would more sensibly be written like this:
Usuario[] user = UserNew();
Now you can't mess up the copy because you're not doing it. You also don't use double the data for no reason.
EDIT:
The code seems worse than I first thought, so I'm going to provide a basic outline of how it ought to work and you can fill in the blanks. You should have method to get the data, e.g.
static User[] GetUsers()
{
var users = new User[5];
for (var i = 0; i < users.Length; i++)
{
// Gather input and set users[i]
}
return users;
}
and a method to display data, e.g.
static void DisplayUsers(User[] users)
{
foreach (var user in users)
{
// Display user
}
}
and then you should call one to get the data and then pass that data into the other, e.g.
var users = GetUsers();
DisplayUsers(users);
You almost certainly wouldn't use an array like that in a real app, because you generally wouldn't know how many users you were going to have. If your assignment requires you to use arrays though, this is pretty much how you should do it.

Related

it isn't cohesive, and it doesn't abstract away any of the implementation details

My professor doesn't want all my code in one class. I am new to C# as well so I don't know how to make my code cohesive and have it abstract away any of the implementation details. Here is my code I have so far. I am trying to make multiple classes because this class has too many responsibilities and I don't know how to do that.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
namespace SvgGenerator
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please enter the name of the output file.");
string outputFile = Console.ReadLine() + ".svg";
Console.WriteLine("Do you want to manually enter the squares or read them from a file? Man or File?");
string fileRead = Console.ReadLine();
if (fileRead.Trim() == "Manually" || fileRead.Trim() == "manually" || fileRead.Trim() == "Man" || fileRead.Trim() == "man")
{
ManInput(outputFile);
}
if (fileRead.Trim() == "file" || fileRead.Trim() == "File")
{
FileInput(outputFile);
}
}
private static void FileInput(string outputFile)
{
Console.WriteLine("What is the name of the file?");
string titleFileName = Console.ReadLine();
StreamReader reader;
reader = new StreamReader(titleFileName);
string textFile = reader.ReadToEnd();
reader.Close();
string[] values = textFile.Split(',', '\n');
List<Square> squares = new List<Square>();
for (int i = 0; i < values.Length;)
{
int valueNumsX = int.Parse(values[i].Trim());
int valueNumsY = int.Parse(values[i + 1].Trim());
Square squareQ = new Square(Color.FromName(values[i + 2].Trim()), valueNumsX, valueNumsY);
squares.Add(squareQ);
if (i == values.Length - 3)
{
SvgBuilder svgBuilder = new SvgBuilder();
string SVG = svgBuilder.Build(squares);
FileCreator Myfilecreater = new FileCreator();
Myfilecreater.Create(outputFile, SVG);
}
i = i + 3;
}
}
private static void ManInput(string outputFile)
{
Console.WriteLine("How many squares do you want in your SVG file?");
string squareCount = Console.ReadLine();
int numSquareCount = Convert.ToInt32(squareCount);
Console.WriteLine("What are the colors of your squares?");
string[] squareColor = new string[numSquareCount];
List<Square> squares = new List<Square>();
for (int i = 0; i < numSquareCount; i++)
{
squareColor[i] = Console.ReadLine();
Square squareQ = new Square(Color.FromName(squareColor[i]), i*4, 0, 200);
squares.Add(squareQ);
if (i == numSquareCount - 1)
{
SvgBuilder svgBuilder = new SvgBuilder();
string SVG = svgBuilder.Build(squares);
FileCreator Myfilecreater = new FileCreator();
Myfilecreater.Create(outputFile, SVG);
}
}
}
}
}`
First of all you should separate classes or methods handling input from classes handling output. If is also typically a poor idea to mix UI from the functional parts, even if the the UI is a console for this case.
I would suggest using the following methods:
private static IEnumerable<Square> ReadSquaresFromFile(string filePath)
private static IEnumerable<Square> ReadSquaresFromConsole()
private static WriteToFile(IEnumerable<Square> squares, string filePath)
For such a simple program procedural programming should be just fine. You do not have to use object. But if you want to, you could for example create a interface like:
public interface ISquareSource(){
IEnumerable<Square> Get();
}
With a file-implementation, console-implementation etc.
Note that I have used string filePath as the file source/destination. If you ever write a library or API, please ensure you have an overlay that takes a stream. It is super annoying to have some data in memory and being forced to write it to a temporary file just because some developer only imagined reading from actual files.
You could also consider using switch statements for handling input, for example
switch(fileRead.Trim().ToLower()){
case "manually":
...
break;
case "file":
...
break;
default:
Console.WriteLine("Invalid input, expected 'manually' or 'file'
break;
Cohesion is the idea that code that does the same thing belongs together, and code that doesn't do the same thing doesn't belong together.
So, let's consider the FileInput function. At a glance, we can see that it does the following:
Prompts the user for a file name to load.
Opens the file.
Reads all of its content into memory.
Closes the file.
Parses the file content into an array of strings.
For each item in the array:
Parses some integral values.
Creates a Square object from those values.
If the index of the current item is equal to the length of the array less 3:
Instantiates a new SvgBuilder.
Invokes its Build method.
Instantiates a new FileCreator.
Invokes its Create method.
There's a lot going on here. Essentially, there are three separate kinds of work going on here that could be broken out into individual functions:
User input (arguably this could be part of the main function).
Call file deserialization function (reads the input file into memory and returns it as an array of strings).
Call main processing function (iterates over the array)
Performs calculations and creates of Square object.
If index of the current item is array length less 3:
Call Build SVG File function
Call File Creation function.
This is what your instructor is getting at.

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);

How to use variable static list when multi user access

this variable works fine if used by one user, but when used by two or more users then the "static" variable will be read by the next user, the first user instance when filling the gridview there are 5 rows of data and I try to access through other browser when entering the page, gridview on the second user already filled 5 rows of data in input by the first user. then how the solution to this problem? please see my code and give me an solutions. thanks.
static List<ServicesModels> _gridPackageDetail = new List<ServicesModels>();
private void AddListAction(string alfa, string beta)
{
ServicesModels data = new ServicesModels()
{
id_service_detail = Guid.NewGuid(),
scope_name = alfa,
detail_name= beta
};
_gridPackageDetail.Add(data);
}
public ActionResult GridPackageDetail()
{
ViewBag.DataListPackage = _gridPackageDetail.OrderBy(a => a.scope_name).ToList();
return PartialView();
}
my code in mvc3 controller.
The code is working fine, because this is what intended by "static", to have the same data for multi users. In your case you need to create a list or dictionary or multi-dimensional array (any data structure you are comfortABLE with) and save the data per use in it, and then retrieve the data when needed based on the user id.
static List<ServicesModels> _gridPackageDetail = new List<ServicesModels>();
private void AddListAction(string alfa, string beta)
{
ServicesModels data = new ServicesModels()
{
id_service_detail = Guid.NewGuid(),
scope_name = alfa,
detail_name= beta,
user_id = getTheID()// Get the id of the user
};
_gridPackageDetail.Add(data);
}
public ActionResult GridPackageDetail()
{
ViewBag.DataListPackage = _gridPackageDetail.OrderBy(a => a.scope_name && user_id ==getTheID()).ToList();
return PartialView();
}
replace getTheID() by your way of getting the id of the user.
This is used if you want to keep the data of all users. else you should remove the static keyword.

Why isnt it deleting the string from the list but it does delete something

So I have no idea why it's not deleting the actual string from the WebsiteList, it's weird because it does delete from the ProxyList.
When debugging it says that it does delete something because the websiteList.Count gets lower after running through webisteList.Remove(website);
But it doesnt delete the string, it keeps looping through the same string.
foreach (var website in websiteList.ToArray())
{
var webSplit = website.Split(')');
foreach (var proxy in proxyList.ToArray())
{
if (proxyList.Count > 0)
{
if(websiteList.Count > 0)
{
var proxySplit = proxy.Split(':');
int Port;
bool convert = Int32.TryParse(proxySplit[1], out Port);
if (this returns true)
{
Console.WriteLine("Removing proxy");
proxyList.Remove(proxy);
websiteList.Remove(website);
}
if (this returns true)
{
Console.WriteLine("Removing proxy");
proxyList.Remove(proxy);
websiteList.Remove(website);
}
}
}
else
break;
}
}
You are repeatedly deleting from the same proxyList (i.e. you are repeating the whole inner loop as many times as there are websites). Why are those 2 loops nested? The websites seem not to be related to the proxies. Only if the proxy list would be extracted from a website, nesting would make sense.
Are these 2 lists supposed to have the same length and to have proxies belonging to websites at the same index? If this is the case, loop using a for-loop and loop in reverse order to avoid messing up the indexes.
for (int i = websiteList.Count - 1; i >= 0; i--) {
if (<condition>) {
proxyList.RemoveAt(i);
websiteList.RemoveAt(i);
}
}
If you had a class for the websites, this would simplify manipulating things belonging together. It also has the advantage that you can add additional logic belonging to websites and proxies (like extracting the port number):
public class Website
{
public string Site { get; set; }
public string Proxy { get; set; }
public int Port {
get {
string[] proxySplit = proxy.Split(':');
int portNo = 0;
if (proxySplit.Length == 2) {
Int32.TryParse(proxySplit[1], out portNo);
}
return portNo;
}
}
}
Now the list is of type List<Website> and contains both, the websites and the proxies
You can delete by using the for loop as before or use LINQ and create a new list containing only the desired items
websiteList = websiteList.Where(w => <condition using w.Site, w.Proxy, w.Port>).ToList();
Note: There is a System.Uri class for the manipulation of uniform resource identifiers. Among other things it can extract the port number. Consider using this class instead of your own.

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. ;-)

Categories

Resources