c# One public Streamreader - c#

I' don't know much about C#.I'm trying to create a program that can calculate results for running competitions. I have created two methods: DoStuff(), with which I set up a Datagridview table columns and displays a text file splitted.And Method- Sorter(), with which I sort the runners into groups and display them in to the List boxes.
My problem.
Every time when i try to debug a program i get a following error:
'Cannot read from a closed TextReader.'
As you can see i set the StreamReader as public. But those two methods can not access it.
Is it possible in some way like this to use one StreamReader?!
Or i have to write my code in Form-Load and then it is the only way to access it?
My Code
public partial class Form1 : Form
{
public StreamReader sr = new StreamReader(#"C:\Users\Oscar\Desktop\result.txt");
public string line;
public List<string> V = new List<string>();
public List<string> V12 = new List<string>();
public List<string> V14 = new List<string>();
public List<string> V18 = new List<string>();
public List<string> V50 = new List<string>();
public List<string> VB = new List<string>();
public List<string> VC = new List<string>();
public List<string> S = new List<string>();
public List<string> S12 = new List<string>();
public List<string> S14 = new List<string>();
public List<string> S18 = new List<string>();
public List<string> S40 = new List<string>();
public Form1()
{
InitializeComponent();
}
public void ReadFile()
{
}
private void Form1_Load(object sender, EventArgs e)
{
SORTER();
Dostuff();
}
public void Dostuff()
{
if (!System.IO.File.Exists(#"C:\Users\Oscar\Desktop\result.txt"))
return;
dataGridView1.ColumnCount = 3;
dataGridView1.Columns[0].HeaderCell.Value = "Number";
dataGridView1.Columns[1].HeaderCell.Value = "name";
dataGridView1.Columns[2].HeaderCell.Value = "time";
using (sr)
while (sr.Peek() > -1 )
{
dataGridView1.Rows.Add(sr.ReadLine().Split(';'));
}
}
public void SORTER()
{
using (sr)
while ((line = sr.ReadLine()) != null)
{
if (line.Contains(";V;")) { V.Add(line); }
else if (line.Contains(";V12;")) { V12.Add(line); }
else if (line.Contains(";V14;")) { V14.Add(line); }
else if (line.Contains(";V18;")) { V18.Add(line); }
else if (line.Contains(";V50;")) { V50.Add(line); }
else if (line.Contains(";S12;")) { S12.Add(line); }
else if (line.Contains(";S14;")) { S14.Add(line); }
else if (line.Contains(";S18;")) { S18.Add(line); }
else if (line.Contains(";S40;")) { S40.Add(line); }
if (line.Contains(";S;")) { S.Add(line); }
}
listBox1.DataSource = V12;
listBox2.DataSource = V50;
listBox3.DataSource = V;
listBox4.DataSource = S;
listBox5.DataSource = S18;
listBox6.DataSource = S40;
}
}

Don't do this inside your Form definition:
public StreamReader sr = new StreamReader(#"C:\Users\Oscar\Desktop\result.txt");
That opens your file once when your form is created and leaves it open. Doing that can block or crash your application if you get an IO error, and often causes “file is in use” errors. You should open a file only when you actually need to use it, then close it immediately after. Wherever you do:
using (sr)
you should instead be doing this:
using (var sr = new StreamReader(#"C:\Users\Oscar\Desktop\result.txt"))
Actually, you should be doing this:
using (var sr = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "result.txt")))

You have your StreamReader in a using block in your SORTER() method. This means after that block ends, the StreamReader will be disposed. When you try to reuse the same StreamReader in your DoStuff() method, you will get the error you described, as that StreamReader is no longer in a state to be read from.
Instead of having a global variable for your StreamReader, you could create these as local variables in each of your methods.

Related

Don't know how to fix "The non-generic type 'ArrayList' cannot be used with type arguments"?

I'm trying to read a text file into an ArrayList. If I include in this line: " ArrayList<String> messages = new ArrayList();", then I get the non-generic error, but if I remove it then I get this error on the return line:
Cannot implicitly convert type 'System.Collections.ArrayList'
using System.Collections;
using System;
using System.IO;
namespace mhpreader
{
internal class NewBaseType
{
public ArrayList messages = new ArrayList();
internal NewMhpReader ReadMessages()
{
{
throw new NotImplementedException();
}
}
}
internal class NewMhpReader : NewBaseType
{
private string _FilePath;
public NewMhpReader(string FilePath)
{
this._FilePath = FilePath;
}
private string line;
public NewMhpReader[] ReadMessages(string nMessages)
{
ArrayList<String> messages = new ArrayList();
//List<TextReader> messages = new List<string>;
using (StreamReader stre = new StreamReader(_FilePath))
{
while ((line = stre.ReadLine()) != null)
{
messages.Add(line);
Console.WriteLine(messages);
}
}
return messages;
}
}
}
A couple of things to help you out a bit:
I would remove ArrayList as others have suggested and replace it with List
your List TextReader needs to be List string
Change your Console.WriteLine(messages) to Console.WriteLine(line)
make sure you are including the Generics namespace in your usings (for List<>).
make sure you return the correct type of List string. This is the one that is causing the compiler error you are seeing.
public List<string> ReadMessages(string nMessages)
{
List<string> messages = new List<string>;
using (StreamReader stre = new StreamReader(_FilePath))
{
while ((line = stre.ReadLine()) != null)
{
messages.Add(line);
Console.WriteLine(line);
}
}
return messages;
}

How to map any csv file to object with 1 method

I'm trying to map CSV file into class object with C#. My problem is that i have 3 different files, but I want to fallow DRY principles. Can someone tell me how to change 'ParseLine' method to make it possible?
C# consol app.
This is how my FileReader looks like:
public class FileReader<T> : IFileReader<T> where T : Entity
{
private readonly ITransactionReader<T> _transactionReader;
public FileReader(ITransactionReader<T> transactionReader)
{
_transactionReader = transactionReader;
}
public List<T> GetInfoFromFile(string filePath)
{
var lines = File.ReadAllLines(filePath);
var genericLines = new List<T>();
foreach (var line in lines)
{
genericLines.Add(_transactionReader.ParseLine(line));
}
return genericLines;
}
}
public interface IFileReader<T> where T : Entity
{
List<T> GetInfoFromFile(string filePath);
}
This is how the object should look like.
public class TransactionReader : ITransactionReader<Transaction>
{
public Transaction ParseLine(string line)
{
var fields = line.Split(";");
var transaction = new Transaction()
{
Id = fields[0],
Month = int.Parse(fields[1]),
Day = int.Parse(fields[2]),
Year = int.Parse(fields[3]),
IncomeSpecification = fields[4],
TransactionAmount = int.Parse(fields[5])
};
return transaction;
}
}
public interface ITransactionReader<T>
{
T ParseLine(string line);
}
This is how I run it for test purposes.
class Program
{
private static readonly string filePath = "C:/Users/<my_name>/Desktop/C# Practice/ERP/ERP/CsvFiles/Transaction.csv";
static void Main(string[] args)
{
ITransactionReader<Transaction> transactionReader = new TransactionReader();
IFileReader<Transaction> fileReader = new FileReader<Transaction>(transactionReader);
List<Transaction> Test()
{
var obj = fileReader.GetInfoFromFile(filePath);
return obj;
}
var list = Test();
}
}
I'm looking to modify that line:
genericLines.Add(_transactionReader.ParseLine(line));
and method arguments to make it open for any CSV fil.
I don't mind to change that composition into something more effective.

How to call a list loaded with text (data) to each instance of class

I'm working on a hotel management program and i'm loading a list of articles that belong to a minibar.
So I have a List on a .txt file,and that list must be part of each instance of a class,then every instance of each class is loaded with another .text file, also on the main program. How do I make each instance of that class
has that list loaded before
I've already tried naming it from the constructor of the class but it wont let me access
class Ejecutiva:Habitacion
{
private List<MiniBar> articulosminibar;
public Ejecutiva(int nrohabitacion, string tipocama, int cantcama,
bool estadodisp, int preciohabitacion) : base(nrohabitacion,
tipocama, cantcama, estadodisp, preciohabitacion)
{
this.articulosminibar = ???? ;
}
internal List<MiniBar> Articulosminibar { get => articulosminibar;}
}
class program
{
static void Main(string[] args)
{
try
{
string linea;
string[] destino_split;
MiniBar miniBarEjecutiva;
StreamReader archivo = new StreamReader("C:\\Users\\USUARIO\\Desktop\\semestre5\\Poo\\ArchivosHotel\\MinibarEjecutiva.txt");
linea = archivo.ReadLine();
while(linea != null)
{
destino_split = linea.Split('|');
miniBarEjecutiva = new MiniBar(destino_split[0], int.Parse(destino_split[1]), int.Parse(destino_split[2]), int.Parse(destino_split[3]));
MinibarEjecutiva.Add(miniBarEjecutiva);
linea = archivo.ReadLine();
}
List<MiniBar> MinibarEjecutiva = new List<MiniBar>();
Ejecutiva ejecutivas;
List<Ejecutiva> listaejecutivas = new List<Ejecutiva>();
StreamReader archivo5 = new StreamReader("C:\\Users\\USUARIO\\Desktop\\semestre5\\Poo\\ArchivosHotel\\Ejecutivas.txt");
linea = archivo.ReadLine();
while (linea != null)
{
destino_split = linea.Split('|');
ejecutivas = new Ejecutiva(int.Parse(destino_split[0]), destino_split[1], int.Parse(destino_split[2]),
Convert.ToBoolean(destino_split[3]), int.Parse(destino_split[4]));
listaejecutivas.Add(ejecutivas);
linea = archivo.ReadLine();
}

Access property/field of a class stored as items in a list [duplicate]

This question already has answers here:
Accessing Property of Class in List<Class>
(4 answers)
Closed 4 years ago.
I am trying to access fields stationName & stationId located in the class Station from main but I am unable to.
Is there a reason for this?
public class Station : Line
{
public string stationName;
public string stationId;
}
public class Line
{
public List<Station> line = new List<Station>();
}
class Function
{
static void Main(string[] argv)
{
using (StreamReader reader = new StreamReader(argv[0]))
{
string temp;
Line line = new Line();
while ((temp = reader.ReadLine()) != null)
{
// error here
line.line.stationName = line;
}
}
}
}
I'll eventually make fields to be properties, but it does not change the problem.
Issue is with this line
line.line.stationName
line is a list. You need to access it with index. For example, for accesing stationame
line.line[index].stationName
In your case, you are adding instances of Station to line. So, you need to
using (StreamReader reader = new StreamReader(argv[0]))
{
string temp;
Line line = new Line();
while ((temp = reader.ReadLine()) != null)
{
line.line.Add(new Station
{
stationName = "value you want to assign"
});
}
}
You are not adding the items in the List<Station>. You will need to create a Station object in the loop and add it in the line collection inside your Line class.
You need to write :
Line line = new Line();
while ((temp = reader.ReadLine()) != null)
{
Station s = new Station();
s.stationName = "stationName";
line.line.Add(s);
}
Better would be use good names of the variables. You can rename it to Stations:
public class Line
{
public List<Station> Stations = new List<Station>();
}
So that your code is more easy to read :
while ((temp = reader.ReadLine()) != null)
{
Station station = new Station();
station.stationName = "stationName";
line.Stations.Add(station);
}
Your complete program should more precisely be like this,
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public class Station
{
public string stationName;
public string stationId;
}
public class Line
{
public List<Station> stations = new List<Station>();
}
class Program
{
static void Main(string[] argv)
{
using (StreamReader reader = new StreamReader(argv[0]))
{
string temp;
Line line = new Line();
int i = 0;
while ((temp = reader.ReadLine()) != null)
{
line.stations.Add(new Station
{
stationName = temp,
stationId = (++i).ToString()
});
}
//For printing
foreach (var station in line.stations)
{
Console.WriteLine(station.stationName);
Console.WriteLine(station.stationId);
}
Console.ReadLine();
}
}
}
}

Adding to List inside a Using block

I have a class which implements IDisposable like such
public class SomeClass : IDisposable
{
private IList<string> _someList = new List<string>();
public IList<string> SomeList
{
get { return _someList; }
}
public void Dispose()
{
_someList = null;
}
}
and a method that uses 'using' blocks to create two instances of this class and add it to a collection like such
private static void Main(string[] args)
{
IList<SomeClass> classes = new List<SomeClass>();
using (var sc = new SomeClass())
{
sc.SomeList.Add("a");
classes.Add(sc);
}
using (var sc = new SomeClass())
{
sc.SomeList.Add("b");
classes.Add(sc);
}
foreach (var a in classes)
{
Console.WriteLine(a.SomeList[0]);
}
Console.ReadLine();
}
Whats happening here is by the time i come to the foreach to iterate through my elements in "classes" the "SomeList" property of both my objects are null.
I do understand that since each using executes Dispose() and in the dispose, i AM null-ing the two lists, they will be null.
My question is how do i achieve this without having to stop null-ing "SomeList" inside my dispose().
thanks
--UPDATE 1
Something closer to real code
base.OnPreRender(e);
Page page = HttpContext.Current.CurrentHandler as Page;
HtmlHead head = (HtmlHead)page.Header;
if (Settings.AreSet)
{
using (var noIndex = new HtmlMeta())
{
noIndex.Name = "somename";
noIndex.Content = "somecontent";
head.Controls.AddAt(0, noIndex);
}
}
using (var machineName = new HtmlMeta())
{
machineName.Name = "somename2";
machineName.Content = "somecontent2";
head.Controls.AddAt(1, machineName);
}
UpdateHeader(head);
--UPDATE #2
A variant of the above class with a string property now, as opposed to a a list.
public class SomeClass2 : IDisposable
{
public string SomeString { get; set; }
public void Dispose()
{
SomeString = string.Empty;
}
}
It still does the same thing for the following method. My 'classes' contains lists of 'SomeClass2' which has 'SomeString' as empty (what i set in my dispose())
IList<SomeClass2> classes = new List<SomeClass2>();
using (var sc = new SomeClass2())
{
sc.SomeString = "a";
classes.Add(sc);
}
using (var sc = new SomeClass2())
{
sc.SomeString = "a";
classes.Add(sc);
}
foreach (var a in classes)
{
Console.WriteLine(a.SomeString);
}
Console.ReadLine();
You don't want Dispose to be called? Just don't use using statement.
Or move the entire logic inside using:
private static void Main(string[] args)
{
IList<SomeClass> classes = new List<SomeClass>();
using (var sc = new SomeClass())
{
sc.SomeList.Add("a");
classes.Add(sc);
using (var sc = new SomeClass())
{
sc.SomeList.Add("b");
classes.Add(sc);
foreach (var a in classes)
{
Console.WriteLine(a.SomeList[0]);
}
}
}
Console.ReadLine();
}
Btw: looks like using IDisposable in that case is pointless. GC would gather unused List<T> objects anyway.

Categories