3

文字列を特定のジェネリック型に変換したいT。これは、基本型または文字列(egintまたはstring)、または基本型または文字列の配列(egint[]またはstring[])のいずれかです。私は次の機能を持っています:

T Str2Val<T>(string str)
{
  return (T)Convert.ChangeType(str, typeof(T));
}

基本的なタイプに適しています。ただしT、配列E[]でありstr、値のコンマ区切りリストであるために失敗します。

Tが配列であるかどうかを簡単に確認できますtypeof(T).IsArray。次に、2つの解決策があります。次のように、スカラーを使用して同じ関数の配列を解析します。

  if (!typeof(T).IsArray)
  {
    return (T)Convert.ChangeType(str, typeof(T));
  }
  else
  {
    // Handle an array
  }

または、2つのオーバーロードされた関数を実装します。1つはジェネリック用T、もう1つはジェネリック用E[]です。ただし、どちらのソリューションも失敗します。else-clauseで配列固有のコードを使用することはできません。これは、スカラーと互換性がある必要があるためです。また、C#は、実際には配列であるE[]場合に適切なオーバーロードを選択できません。T

私は何をすべきか?

4

3 に答える 3

7

とにかくカスタムルールを使用したいと思われるので、登録して後で活用するカスタムパーサーのリストを作成します。

public static class StringParsers
{
    private static Dictionary<Type, object> Parsers = new Dictionary<Type, object>();

    public static void RegisterParser<T>(Func<string, T> parseFunction)
    {
        Parsers[typeof(T)] = parseFunction;
    }

    public static T Parse<T>(string input)
    {
        object untypedParser;
        if (!Parsers.TryGetValue(typeof(T), out untypedParser))
            throw new Exception("Could not find a parser for type " + typeof(T).FullName);

        Func<string, T> parser = (Func<string, T>)untypedParser;

        return parser(input);
    }
}

アプリケーションの初期化中に、後で使用する予定の型をアプリケーションに登録します(ジェネリックスを使用しているため、これは既知であると思います)。

StringParsers.RegisterParser<string[]>(input => input.Split(','));
StringParsers.RegisterParser<int[]>(input => input.Split(',').Select(i => Int32.Parse(i)).ToArray());
StringParsers.RegisterParser<int>(input => Int32.Parse(input));

最後に、あなたはそれを簡単に呼ぶことができます:

string testArrayInput = "1,2,8";

int[] integers = StringParsers.Parse<int[]>(testArrayInput); // {1, 2, 8}

string[] strings = StringParsers.Parse<string[]>(testArrayInput); // {"1", "2", "8"}

int singleInt = StringParsers.Parse<int>("9999"); //9999

さて、これは非常に単純な実装です。Func<string, T>タイプを使用する代わりにインターフェイスを使用し、IStringParser必要に応じて解析のより深い実装を提供できるように、拡張することをお勧めします。さらに、スレッドセーフにすることもできます(問題が発生しないことが確実な場合、または起動時の登録が使用であることが確実な場合を除く)

編集:本当に、本当に、本当にすべてを1つの関数にまとめて、コンマ区切りの配列を考慮したい場合、次を使用できます。

public static T Str2Val<T>(string str)
{
    if (!typeof(T).IsArray)
        return (T)Convert.ChangeType(str, typeof(T));

    Type elementType = typeof(T).GetElementType();

    string[] entries = str.Split(',');
    int numberOfEntries = entries.Length;

    System.Array array = Array.CreateInstance(elementType, numberOfEntries);

    for(int i = 0; i < numberOfEntries; i++)
        array.SetValue(Convert.ChangeType(entries[i], elementType), i);

    return (T)(object)array;
}

しかし、これはとても間違っていると感じます。アレクサンダーの答えに二重の一般的な入力を避けるためのより良い方法があるはずですが、そこに行きます。

于 2013-01-10T12:03:40.380 に答える
3

明示的な要素タイプを渡す別のソリューション:

static T Str2Val<T>(string str)
{
    return (T)Convert.ChangeType(str, typeof(T));
}

static E[] Str2Val<T, E>(string str)
    where T : ICollection<E>
{
    return str.Split(',').Select(x => (E)Convert.ChangeType(x, typeof(E))).ToArray();
}

を使用して

Str2Val<int>("1")
Str2Val<int[], int>("1,3")
于 2013-01-10T12:13:34.677 に答える
0

たぶん、このようなものが役立つでしょう:

dynamic Str2Val<T>(string str)
{
    if (!typeof(T).IsArray)
        return (T)Convert.ChangeType(str, typeof(T));

    var elementType = typeof(T).GetElementType();
    return str.Split(',').Select(x => Convert.ChangeType(x, elementType)).ToArray();
}
于 2013-01-10T12:05:57.890 に答える