3

ファイルから値を読み込んだ後、一般的なリストを返そうとしています。しかし、型操作をいじった後でも、私はそれを私に同意させることができません。コードは以下のとおりです。私の質問は次のとおりです。

  1. 以下で始めているように、すべてのキーの種類を識別する必要がありますか、それとももっと簡単な方法がありますか? ここでは「where T: ...」が関連している可能性がありますが、可能であればDateTime、int、string、doubleなどを許可したいのですが、「where」でそれらを行う方法がわかりません。
  2. 日時のリストに DateTime 項目を追加できないのはなぜですか?
  3. タイプ(listType)を取得しようとすると、これは範囲外のようです。上記の行で使用する型を宣言しても、そのようなオブジェクトは存在しないと表示されます。

あなたの考えに感謝します

public static List<T> FileToGenericList<T>(string FilePath, int ignoreFirstXLines = 0, bool stripQuotes = true)
{
    List<T> output = new List<T>();

    Type listType = output.GetType().GetGenericArguments()[0];

    try
    {
        using (StreamReader stream = new StreamReader(File.Open(FilePath, FileMode.Open)))
        {
            string line;
            int currentLine = 0;

            while ((line = stream.ReadLine()) != null)
            {
                // Skip first x lines
                if (currentLine < ignoreFirstXLines) continue;

                // Remove quotes if needed
                if (stripQuotes == true)
                {
                    line = line.Replace(@"""", @"");
                }

                // Q1 - DO I HAVE TO HAVE THIS FOR EACH TYPE OR IS THERE A QUICKER WAY
                if (listType == typeof(System.DateTime))
                {
                    DateTime val = new System.DateTime();
                    val = DateTime.Parse(line);

                    // Q2 ERROR: 'Argument type is not assignable to parameter type 'T''                    
                    output.Add(val);

                    // For some reason the type 'listType' from above is now out of scope when I try a cast
                    output.Add((listType)val);
                }
                if (listType == typeof(System.String))
                {
                    //DateTime val = new System.DateTime();
                    //val = DateTime.Parse(line);
                    //output.Add(val.ToString());
                }

                // Continue tracking for line skipping purposes
                currentLine++;
            }
        }
    }
    catch (Exception ex)
    {
        throw new Exception("Error - there was a problem reading the file at " + FilePath + ".  Error details: " + ex.Message);
    }    
    return output;
}
4

3 に答える 3

3

解析ロジックをFileToGenericListメソッドにコーディングする代わりに、これをリファクタリングしてラムダとして渡すことが、よりクリーンで柔軟なアプローチになると思います。このアプローチを示す簡単なコンソール アプリを次に示します。

class Program
{
    static void Main(string[] args)
    {
        // second argument is a lambda that describes how to convert the line into the type you require
        var dateList = FileToGenericList<DateTime>("dates.txt", DateTime.Parse);
        var stringList = FileToGenericList<string>("strings.txt", s => s);
        var intList = FileToGenericList<int>("integers.txt", Int32.Parse); 

        Console.ReadLine();
    }

    static List<T> FileToGenericList<T>(string filePath, Func<string, T> parseFunc, int ignoreFirstXLines = 0, bool stripQuotes = true)
    {
        var output = new List<T>();

        try
        {
            using (StreamReader stream = new StreamReader(File.Open(filePath, FileMode.Open)))
            {
                string line;
                int currentLine = 0;

                while ((line = stream.ReadLine()) != null)
                {
                    // Skip first x lines
                    if (currentLine < ignoreFirstXLines)
                        continue;

                    // Remove quotes if needed
                    if (stripQuotes == true)
                        line = line.Replace(@"""", @"");

                    var parsedValue = parseFunc(line);
                    output.Add(parsedValue);
                    currentLine++;
                }
            }
        }
        catch (Exception ex)
        {
            throw new Exception("Error - there was a problem reading the file at " + FilePath + ".  Error details: " + ex.Message);
        }    
        return output;
   }
}
于 2012-04-23T06:19:56.250 に答える
2

// Q1 - タイプごとにこれを用意する必要がありますか、それともより迅速な方法がありますか

開始するためのテストコードを次に示します。

using System;
using System.Collections.Generic;

namespace AddGenericToList
{
    class Program
    {
        static void Main(string[] args)
        {
            var tc = new ListClass<string>();

            tc.Add("a value");
            tc.Add(123);
            tc.Add(DateTime.Now);
        }
    }

    internal class ListClass<T>
    {
        private readonly List<T> list = new List<T>();

        public void Add(object value)
        {
            list.Add((T)Convert.ChangeType(value, Nullable.GetUnderlyingType(typeof (T)) ?? typeof (T)));
        }
    }
}

ただし、無効なキャストはエラーをスローします。たとえば、DateTimeは に変換できますが、 には変換できstringませんint

于 2012-04-23T05:48:19.910 に答える
0

質問3の場合:「スコープ外」エラーが発生する理由は、変数にキャストできないためです。あなたoutput.Add((listType)val);は正当なC#ステートメントではありません-明示的な型定義にのみキャストできます。幸いなことに、明示的な型定義(ジェネリックパラメーターとして取得したT )があるType listTypeため、変数を介してすべてのキャストを行う必要はありません。@Pravin Pawarの答えの奥深くで答えを見ることができます。Tは必ずしも参照型ではないため、明示的なキャスト構文を使用することをお勧めします。output.Add(val as T);output.Add((T)val)

編集:

(T)val以前にチェックしたにもかかわらず、コンパイラーは私たちのために余分な努力をせず、TがDateTimeであると決定するので、あなたはそれがコンパイルされないのは正しいです。だからあなたはこれを行うことができます:

 (T)Convert.ChangeType(val, typeof(T)));

これにより、DateTime valがT(これはDateTimeでもあります)に変換されます。これは、コンパイラーを満足させるのに十分です。

于 2012-04-23T05:46:20.820 に答える