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

时间:2012-07-29 11:39:18

标签: c# unit-testing c#-4.0 generics

我遇到过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;
        }
    }

3 个答案:

答案 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");