I've come across a post where Jon Skeet posted the following:
You can't use any parameterised constructor. You can use a parameterless constructor if you have a "where T : new()" constraint.
I have the following method:
public T ReturnReader<T>(String filePath) where T : TextReader, new()
{
return new T(filePath);
}
which violates what he mentions above. I need the above method to be able to return both a StreamReader (production code) and a StringReader (unit testing purposes). As far as i can see, streamReader doesnt contain a parameterless constructor so i cant create a parameterless instance and then assign the filePath to it in the calling method.
Anyone see a solution to this?
Thanks for your time!
EDIT: original Methods, A and B
/// <summary>
/// Ensures number of columns stated == number of columns in file.
/// </summary>
/// <param name="errorMessageList">A running list of all errors encountered.</param>
public static void ValidateNumberOfColumns(string filePath, int userSpecifiedColumnCount, List<String> errorMessageList)
{
int numberOfColumnsInFile = GetNumberOfColumnsInFile(filePath, errorMessageList);
if (userSpecifiedColumnCount != numberOfColumnsInFile) errorMessageList.Add("Number of columns specified does not match number present in file.");
}
public static int GetNumberOfColumnsInFile(string filePath, List<String> errorMessageList)
{
int numberOfColumns = 0;
string lineElements = null;
try
{
using (StreamReader columnReader = new StreamReader(filePath))
{
lineElements = columnReader.ReadLine();
string[] columns = lineElements.Split(',');
numberOfColumns = columns.Length;
}
return numberOfColumns;
}
catch (Exception ex)
{
errorMessageList.Add(ex.Message);
return -1;
}
}
If you have to resort to reflection to make code feasible for unit testing, that's a bit of a smell - only an indication that you may want to reconsider the design, but certainly something to think about.
I would be tempted to abstract this into a separate interface - IPathReader
or something similar:
public interface IPathReader
{
TextReader CreateReader(string path);
}
Then inject that into the class that you're testing - with an implementation using StringReader
in testing, and an implementation using StreamReader
in production. (I suspect you don't really need it to return different types.)
Note that this is effectively just a Func<string, TextReader>
- so you could use that instead of an interface if you wanted.
Activator.CreateInstance
it's not good idea because it's too expensive, so you could try this
StringReader reader = ReturnReader(() => new StringReader(filePath));
StreamReader streamReader = ReturnReader(() => new StreamReader(filePath));
private T ReturnReader<T>(Func<T> reader)
where T : TextReader
{
return reader();
}
EDIT
as per code, I think the best way is to separate NumberOfColumns and getting first row. So the new method NumberOfColumns will not depend from stream readers
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