我遇到过Jon Skeet发布以下内容的帖子:
您不能使用任何参数化构造函数。你可以使用 无参数构造函数,如果你有“where T:new()”约束。
我有以下方法:
public T ReturnReader<T>(String filePath) where T : TextReader, new()
{
return new T(filePath);
}
这违反了他上面提到的内容。我需要上面的方法才能返回StreamReader(生产代码)和StringReader(单元测试目的)。据我所知,streamReader不包含无参数构造函数,因此我无法创建无参数实例,然后在调用方法中为其分配filePath。
任何人都能看到解决方案吗?
谢谢你的时间!
编辑:原始方法,A和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;
}
}
答案 0 :(得分:7)
如果您不得不求助于反射以使代码可用于单元测试,这有点气味 - 只有指示您可能想重新考虑设计,但当然要考虑一下
我很想把它抽象成一个单独的界面 - IPathReader
或类似的东西:
public interface IPathReader
{
TextReader CreateReader(string path);
}
然后将其注入您正在测试的类中 - 在测试中使用StringReader
的实现,以及在生产中使用StreamReader
的实现。 (我怀疑你真的不需要它来返回不同的类型。)
请注意,这实际上只是Func<string, TextReader>
- 所以如果您愿意,可以使用它而不是接口。
答案 1 :(得分:5)
Activator.CreateInstance
这不是一个好主意,因为它太贵了,所以你可以试试这个
StringReader reader = ReturnReader(() => new StringReader(filePath));
StreamReader streamReader = ReturnReader(() => new StreamReader(filePath));
private T ReturnReader<T>(Func<T> reader)
where T : TextReader
{
return reader();
}
修改强>
根据代码,我认为最好的方法是将NumberOfColumns与第一行分开。因此,新方法NumberOfColumns将不依赖于流读取器
答案 2 :(得分:3)
您可以使用反射轻松完成此操作:
public T ReturnReader<T>(String filePath) where T : TextReader
{
return (T)Activator.CreateInstance(typeof(T),filePath);
}
用法:
var rdr = ReturnReader<StringReader>("c:\\test.txt");
var rdr2 = ReturnReader<StreamReader>("c:\\test.txt");