はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】TypeConverter.ConvertFromStringメソッドを用いてstringからgenericな型にParseでき、C#11からはIParsable<TSelf>が使える

はじめに

今回はTypeConverter.ConvertFromStringとC#11から導入されたIParsable<TSelf>を紹介したいと思います。

learn.microsoft.com

learn.microsoft.com

概要

例えばstringからジェネリックのTの値にパースしたいとします。

public static bool TryParse<T>(string value, out T result)
{
    // TODO: valueからresultにParseをする
}

ぱっと見型スイッチで頑張るみたいなものしか思いつかなかったのですが、これを実現する方法について調べたので書き残しておきたいと思います。

C#11以前

TypeConverter.ConvertFromStringを用いることで指定したテキストをオブジェクトに変換できます。
learn.microsoft.com

public static bool TryParse<T>(string value, out T result) where T : struct
{
    try
    {
        // TypeConverterが型をサポートしていないと「System.NotSupportedException」が投げられる
        TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
                
        // Parseに失敗すると「System.ArgumentException」が投げられる
        result = (T)(converter.ConvertFromString(value) ?? default(T));
        return true;
    }
    catch
    {
        result = default;
        return false;
    }
}

正直エラーが投げられるのは微妙ですね。TryConvertFromStringみたいなメソッドもなければ、パースできるか判定するメソッドもありません。

パフォーマンス的にも正直微妙そうです。

C#11以降

IParsable<T>というインターフェースが導入されました。

public interface IParsable<TSelf>
    where TSelf : IParsable<TSelf>?
{
    static abstract TSelf Parse(string s, IFormatProvider? provider);

    static abstract bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(returnValue: false)] out TSelf result);
}

learn.microsoft.com

public static bool TryParse<T>(string value, out T result) where T : IParsable<T>
{
    return T.TryParse(value, null, out result);
}

こちらはかなり実用的だと思います。

パフォーマンス的な話

特にC#11以前の場合の手法はパフォーマンスが悪そうです。具体的な型を引数にとったメソッドを複数生やした方が良いかもしれません。

public static bool TryParseInt(string value, out int result)
{
    return int.TryParse(value, out result);
}
// ...