4

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

3 回答 3

7

如果您必须求助于反射来使代码可用于单元测试,那有点异味 - 只是表明您可能想要重新考虑设计,但肯定需要考虑一些事情。

我很想将其抽象为一个单独的接口 -IPathReader或类似的东西:

public interface IPathReader
{
    TextReader CreateReader(string path);
}

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

请注意,这实际上只是一个Func<string, TextReader>- 因此如果需要,您可以使用它而不是界面。

于 2012-07-29T12:10:10.500 回答
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 将不依赖于流阅读器

于 2012-07-29T12:09:11.997 回答
4

您可以通过使用反射轻松完成此操作:

    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");
于 2012-07-29T11:51:27.623 回答