I'm trying to return a generic list after loading values from a file. However, after much fiddling with type manipulations I still can't get it to agree with me. The code is below; my questions are:
Many thanks for your thoughts
public static List<T> FileToGenericList<T>(string FilePath, int ignoreFirstXLines = 0, bool stripQuotes = true)
{
List<T> output = new List<T>();
Type listType = output.GetType().GetGenericArguments()[0];
try
{
using (StreamReader stream = new StreamReader(File.Open(FilePath, FileMode.Open)))
{
string line;
int currentLine = 0;
while ((line = stream.ReadLine()) != null)
{
// Skip first x lines
if (currentLine < ignoreFirstXLines) continue;
// Remove quotes if needed
if (stripQuotes == true)
{
line = line.Replace(@"""", @"");
}
// Q1 - DO I HAVE TO HAVE THIS FOR EACH TYPE OR IS THERE A QUICKER WAY
if (listType == typeof(System.DateTime))
{
DateTime val = new System.DateTime();
val = DateTime.Parse(line);
// Q2 ERROR: 'Argument type is not assignable to parameter type 'T''
output.Add(val);
// For some reason the type 'listType' from above is now out of scope when I try a cast
output.Add((listType)val);
}
if (listType == typeof(System.String))
{
//DateTime val = new System.DateTime();
//val = DateTime.Parse(line);
//output.Add(val.ToString());
}
// Continue tracking for line skipping purposes
currentLine++;
}
}
}
catch (Exception ex)
{
throw new Exception("Error - there was a problem reading the file at " + FilePath + ". Error details: " + ex.Message);
}
return output;
}
Instead of coding your parsing logic into your FileToGenericList method, I think a cleaner and more flexible approach would be to refactor this out and pass it in as a lambda. Here is a quick console app that demonstrates this approach:
class Program
{
static void Main(string[] args)
{
// second argument is a lambda that describes how to convert the line into the type you require
var dateList = FileToGenericList<DateTime>("dates.txt", DateTime.Parse);
var stringList = FileToGenericList<string>("strings.txt", s => s);
var intList = FileToGenericList<int>("integers.txt", Int32.Parse);
Console.ReadLine();
}
static List<T> FileToGenericList<T>(string filePath, Func<string, T> parseFunc, int ignoreFirstXLines = 0, bool stripQuotes = true)
{
var output = new List<T>();
try
{
using (StreamReader stream = new StreamReader(File.Open(filePath, FileMode.Open)))
{
string line;
int currentLine = 0;
while ((line = stream.ReadLine()) != null)
{
// Skip first x lines
if (currentLine < ignoreFirstXLines)
continue;
// Remove quotes if needed
if (stripQuotes == true)
line = line.Replace(@"""", @"");
var parsedValue = parseFunc(line);
output.Add(parsedValue);
currentLine++;
}
}
}
catch (Exception ex)
{
throw new Exception("Error - there was a problem reading the file at " + FilePath + ". Error details: " + ex.Message);
}
return output;
}
}
// Q1 - DO I HAVE TO HAVE THIS FOR EACH TYPE OR IS THERE A QUICKER WAY
Here is some test code to get you started:
using System;
using System.Collections.Generic;
namespace AddGenericToList
{
class Program
{
static void Main(string[] args)
{
var tc = new ListClass<string>();
tc.Add("a value");
tc.Add(123);
tc.Add(DateTime.Now);
}
}
internal class ListClass<T>
{
private readonly List<T> list = new List<T>();
public void Add(object value)
{
list.Add((T)Convert.ChangeType(value, Nullable.GetUnderlyingType(typeof (T)) ?? typeof (T)));
}
}
}
However, invalid casts will throw an error. For instance, DateTime can be converted to string but not to int.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With