web-dev-qa-db-ja.com

C#:System.Typeからの動的解析

Type、String、およびObjectがあります。

解析メソッドを呼び出したり、文字列でそのタイプを動的に変換する方法はありますか?

基本的に、このロジックのifステートメントを削除するにはどうすればよいですか

object value = new object();    
String myString = "something";
Type propType = p.PropertyType;

if(propType == Type.GetType("DateTime"))
{
    value = DateTime.Parse(myString);
}

if (propType == Type.GetType("int"))
{
    value = int.Parse(myString);
}

そして、このようなもっと何かをします。

object value = new object();
String myString = "something";
Type propType = p.PropertyType;


//this doesn't actually work
value = propType .Parse(myString);  
47
ctrlShiftBryan

TypeDescriptor 救助する:

var converter = TypeDescriptor.GetConverter(propType);
var result = converter.ConvertFrom(myString);

TypeConverterインフラストラクチャに統合するには、独自の TypeConverter を実装し、 TypeConverterAttribute で変換されるクラスを修飾します

80
Anton Gogolev

これは、すべてのプリミティブ型、およびIConvertibleを実装する型で機能するはずです

public static T ConvertTo<T>(object value)
{
    return (T)Convert.ChangeType(value, typeof(T));
}

編集:実際には、あなたの場合、ジェネリックは使用できません(少なくとも簡単ではありません)。代わりにそれを行うことができます:

object value = Convert.ChangeType(myString, propType);
15
Thomas Levesque

私はこの問題に遭遇し、これが私がそれを解決した方法です:

value = myString;
var parse = propType.GetMethod("Parse", new[] { typeof(string) });
if (parse != null) {
  value = parse.Invoke(null, new object[] { value });
}

...そしてそれは私のために働いた。

要約すると、引数として1つの文字列のみをとるオブジェクトタイプの静的な「Parse」メソッドを見つけようとしています。そのようなメソッドが見つかった場合は、変換しようとしている文字列パラメーターでそのメソッドを呼び出します。 pは私のタイプのPropertyInfoであるため、次のように値をインスタンスに設定してこのメ​​ソッドを終了しました。

p.SetValue(instance, value, null);
7
Randall Borck

何を達成したいかによって異なります。

1)コードをクリーンアップし、繰り返し型チェックを削除しようとしている場合は、チェックをメソッド、commeに集中化する必要があります

public static T To<T> (this string stringValue)
{
    T value = default (T);

    if (typeof (T) == typeof (DateTime))
    {
        // insert custom or convention System.DateTime 
        // deserialization here ...
    }
    // ... add other explicit support here
    else
    {
        throw new NotSupportedException (
            string.Format (
            "Cannot convert type [{0}] with value [{1}] to type [{2}]." + 
            " [{2}] is not supported.",
            stringValue.GetType (),
            stringValue,
            typeof (T)));
    }

    return value;
}

2)基本的なタイプでより一般化されたものが必要な場合は、 Thomas Levesquesuggests のようなものを試してみてください-実際には、私はこれを試したことはありませんConvertの[最近の?]拡張また、非常に良い提案。

3)実際には、1)と2)の両方を、基本的な値変換と明示的な複合型のサポートをサポートできる単一の拡張機能にマージする可能性があります。

4)完全に「ハンズフリー」にしたい場合は、デフォルトの単純なデシリアライゼーション[XmlまたはBinary、または両方]をデフォルトにすることもできます。もちろん、これにより入力が制限されます。つまり、すべての入力は適切なXML形式またはバイナリ形式でなければなりません。正直なところ、これはおそらくやり過ぎですが、言及する価値があります。

もちろん、これらのメソッドはすべて本質的に同じことを行います。それらのいずれにも魔法はなく、ある時点でsomeoneは線形ルックアップを実行します(シーケンシャルif節を介した暗黙的なルックアップであるか、.Net変換およびシリアル化機能を介したフードの下で)。 。

5)パフォーマンスを改善したい場合は、変換プロセスの「ルックアップ」部分を改善する必要があります。明示的な「サポートされるタイプ」リストを作成します。各タイプは配列内のインデックスに対応します。呼び出しでTypeを指定する代わりに、インデックスを指定します。

EDIT:したがって、線形ルックアップはきちんと高速ですが、消費者が単に変換関数を取得して呼び出した場合、それはさらに高速になると思います直接。つまり、消費者はどのタイプに変換するかを知っているので[これは与えられている]なので、一度に多くのアイテムを変換する必要がある場合、

// S == source type
// T == target type
public interface IConvert<S>
{
    // consumers\infrastructure may now add support
    int AddConversion<T> (Func<S, T> conversion);

    // gets conversion method for local consumption
    Func<S, T> GetConversion<T> ();

    // easy to use, linear look up for one-off conversions
    T To<T> (S value);
}

public class Convert<S> : IConvert<S>
{

    private class ConversionRule
    {
        public Type SupportedType { get; set; }
        public Func<S, object> Conversion { get; set; }
    }

    private readonly List<ConversionRule> _map = new List<ConversionRule> ();
    private readonly object _syncRoot = new object ();

    public void AddConversion<T> (Func<S, T> conversion)
    {
        lock (_syncRoot)
        {
            if (_map.Any (c => c.SupportedType.Equals (typeof (T))))
            {
                throw new ArgumentException (
                    string.Format (
                    "Conversion from [{0}] to [{1}] already exists. " +
                    "Cannot add new conversion.", 
                    typeof (S), 
                    typeof (T)));
            }

            ConversionRule conversionRule = new ConversionRule
            {
                SupportedType = typeof(T),
                Conversion = (s) => conversion (s),
            };
            _map.Add (conversionRule);
        }
    }

    public Func<S, T> GetConversion<T> ()
    {
        Func<S, T> conversionMethod = null;

        lock (_syncRoot)
        {
            ConversionRule conversion = _map.
                SingleOrDefault (c => c.SupportedType.Equals (typeof (T)));

            if (conversion == null)
            {
                throw new NotSupportedException (
                    string.Format (
                    "Conversion from [{0}] to [{1}] is not supported. " + 
                    "Cannot get conversion.", 
                    typeof (S), 
                    typeof (T)));
            }

            conversionMethod = 
                (value) => ConvertWrap<T> (conversion.Conversion, value);
        }

        return conversionMethod;
    }

    public T To<T> (S value)
    {
        Func<S, T> conversion = GetConversion<T> ();
        T typedValue = conversion (value);
        return typedValue;
    }

    // private methods

    private T ConvertWrap<T> (Func<S, object> conversion, S value)
    {
        object untypedValue = null;
        try
        {
            untypedValue = conversion (value);
        }
        catch (Exception exception)
        {
            throw new ArgumentException (
                string.Format (
                "Unexpected exception encountered during conversion. " +
                "Cannot convert [{0}] [{1}] to [{2}].",
                typeof (S),
                value,
                typeof (T)),
                exception);
        }

        if (!(untypedValue is T))
        {
            throw new InvalidCastException (
                string.Format (
                "Converted [{0}] [{1}] to [{2}] [{3}], " +
                "not of expected type [{4}]. Conversion failed.",
                typeof (S),
                value,
                untypedValue.GetType (),
                untypedValue,
                typeof (T)));
        }

        T typedValue = (T)(untypedValue);

        return typedValue;
    }

}

そしてそれは

// as part of application innitialization
IConvert<string> stringConverter = container.Resolve<IConvert<string>> ();
stringConverter.AddConversion<int> (s => Convert.ToInt32 (s));
stringConverter.AddConversion<Color> (s => CustomColorParser (s));

...

// a consumer elsewhere in code, say a Command acting on 
// string input fields of a form
// 
// NOTE: stringConverter could be injected as part of DI
// framework, or obtained directly from IoC container as above
int someCount = stringConverter.To<int> (someCountString);

Func<string, Color> ToColor = stringConverter.GetConversion <Color> ();
IEnumerable<Color> colors = colorStrings.Select (s => ToColor (s));

complete変換を制御できるので、この後者のアプローチが非常に好きです。 Castle WindsorやUnityなどのInversion of Control [IoC]コンテナーを使用する場合、このサービスの注入は自動的に行われます。また、instanceベースであるため、それぞれ独自の変換ルールのセットを持つ複数のインスタンスを持つことができます-たとえば、複数のユーザーコントロールがあり、それぞれが独自のDateTimeまたはその他の複雑な文字列形式を生成する場合。

一体、単一のターゲットタイプに対して複数の変換ルールをサポートしたい場合でも、それは可能ですが、メソッドパラメーターを拡張して、どのパラメーターを指定するかを指定するだけです。

2
johnny g

文字列を見て、それがどのタイプを表しているかを確実に知ることは技術的に不可能です。

したがって、一般的なアプローチでは、少なくとも次のものが必要です。

  1. 解析される文字列
  2. 解析に使用されるタイプ。

静的Convert.ChangeType()メソッドを見てください。

0
user286353