通用方法,是否可以返回StreamReader实例?

Han*_*del 4 c# generics unit-testing c#-4.0

我遇到过Jon Skeet发布以下内容的帖子:

您不能使用任何参数化构造函数.如果您有"where T:new()"约束,则可以使用无参数构造函数.

我有以下方法:

public T ReturnReader<T>(String filePath) where T : TextReader, new()
{
    return new T(filePath);
}
Run Code Online (Sandbox Code Playgroud)

这违反了他上面提到的内容.我需要上面的方法才能返回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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 7

如果你不得不求助于反射以使代码可用于单元测试,这有点气味 - 只是表明你可能想重新考虑设计,但肯定要考虑一些事情.

我很想把它抽象成一个单独的界面 - IPathReader或类似的东西:

public interface IPathReader
{
    TextReader CreateReader(string path);
}
Run Code Online (Sandbox Code Playgroud)

然后将其注入您正在测试的类中 - 使用StringReader测试中的实现,以及StreamReader在生产中使用的实现.(我怀疑你真的不需要它来返回不同的类型.)

请注意,这实际上只是Func<string, TextReader>- 如果您愿意,可以使用它而不是接口.


GSe*_*rjo 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();
    }
Run Code Online (Sandbox Code Playgroud)

编辑

根据代码,我认为最好的方法是将NumberOfColumns和第一行分开.因此,新方法NumberOfColumns将不依赖于流读取器