4

JonSkeetが次のように投稿した投稿に出くわしました。

パラメーター化されたコンストラクターは使用できません。「whereT: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 に答える