web-dev-qa-db-ja.com

C#汎用文字列を任意のオブジェクトに解析

オブジェクト値を文字列に保存しています。たとえば、

string[] values = new string[] { "213.4", "10", "hello", "MyValue"};

適切なオブジェクト型を一般的に初期化する方法はありますか?たとえば、次のようなもの

double foo1 = AwesomeFunction(values[0]);
int foo2 = AwesomeFunction(values[1]);
string foo3 = AwesomeFunction(values[2]);
MyEnum foo4 = AwesomeFunction(values[3]);

ここで、AwesomeFunctionは必要な関数です。最終的な用途は、プロパティを初期化することです。

MyObject obj = new MyObject();
PropertyInfo info = typeof(MyObject).GetProperty("SomeProperty");
info.SetValue(obj, AwesomeFunction("20.53"), null);

このような機能が必要なのは、データベースに上記の値を保存していて、クエリを介してそれらの値を読み取り、オブジェクトの対応するプロパティを初期化したいからです。これは可能でしょうか?オブジェクト全体がデータベースに格納されているのではなく、動的に読み取りおよび設定したいいくつかのフィールドだけです。私は静的にそれを行うことができることを知っていますが、それは面倒になり、保守が難しくなり、多数の異なるフィールド/プロパティとの間違いが発生しやすくなります。

編集:AwesomeFunctionが文字列を受け取るコンストラクターを指定するカスタムクラスで動作する場合、ボーナスポイント!

EDIT2:このタイプの機能を使用したい特定のケースでは、宛先タイプはPropertyTypeを介して知ることができます。 Enumsはこれで解析しやすいと思います、例えば、

Type destinationType = info.PropertyType;
Enum.Parse(destinationType, "MyValue");
27
mike

おそらく最初に試すことは:

object value = Convert.ChangeType(text, info.PropertyType);

ただし、これはカスタムタイプによる拡張性をサポートしていません。 必要な場合、どうですか:

TypeConverter tc = TypeDescriptor.GetConverter(info.PropertyType);
object value = tc.ConvertFromString(null, CultureInfo.InvariantCulture, text);
info.SetValue(obj, value, null);

または:

info.SetValue(obj, AwesomeFunction("20.53", info.PropertyType), null);

public object AwesomeFunction(string text, Type type) {
    TypeConverter tc = TypeDescriptor.GetConverter(type);
    return tc.ConvertFromString(null, CultureInfo.InvariantCulture, text);
}
37
Marc Gravell
public T Get<T>(string val)
{
    if (!string.IsNullOrWhiteSpace(val))
        return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromString(val);
    else 
        return default(T);
}
4
RouR

ここに簡単なバージョンがあります:

object ConvertToAny(string input)
{
    int i;
    if (int.TryParse(input, out i))
        return i;
    double d;
    if (double.TryParse(input, out d))
        return d;
    return input;
}

Intとdoubleを認識しますが、それ以外はすべて文字列として返されます。 enumの処理に関する問題は、値がどのenumに属しているかを知る方法がなく、文字列であるかどうかを判断する方法がないことです。その他の問題は、日付/時刻または小数を処理しないことです(それらをdoubleからどのように区別しますか?)など。

次のようにコードを変更する場合:

PropertyInfo info = typeof(MyObject).GetProperty("SomeProperty"); 
info.SetValue(obj, AwesomeFunction("20.53", info.PropertyType), null); 

その後、大幅に簡単になります。

object ConvertToAny(string input, Type target)
{
    // handle common types
    if (target == typeof(int))
        return int.Parse(input);
    if (target == typeof(double))
        return double.Parse(input);
    ...
    // handle enums
    if (target.BaseType == typeof(Enum))
        return Enum.Parse(target, input);
    // handle anything with a static Parse(string) function
    var parse = target.GetMethod("Parse",
                    System.Reflection.BindingFlags.Static |
                    System.Reflection.BindingFlags.Public,
                    null, new[] { typeof(string) }, null);
    if (parse != null)
        return parse.Invoke(null, new object[] { input });
    // handle types with constructors that take a string
    var constructor = target.GetConstructor(new[] { typeof(string) });
    if (constructor != null)
        return constructor.Invoke(new object[] { input });
}

編集:不足している括弧を追加

3
Gabe

これであなたの質問に答えられないことはわかっていますが、 Dapper micro ORM を見たことはありますか?
それは非常に単純で(LINQ to SQLまたはそのため、Entity Frameworkと比較して)、必要なことを行います。

このことを考慮:

public class Dog
{
    public int? Age { get; set; }
    public Guid Id { get; set; }
    public string Name { get; set; }
    public float? Weight { get; set; }    
}            

var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select * from Dogs where Id = @Id",
    new { Id = 42 }
).First();

Dapper自体は 単一のファイル にパッケージ化されており、伝えられるところによると StackOverflowチームが使用 (LinqからSQLを除いて)。

0
Dan Abramov