2011-04-22 37 views
1

今、XMLから構成データを読み込むための普遍的な方法を書いています。私の場合は多くのパラメータは、ノードの属性に保存されているので、私は読ん属性のための普遍的な方法を書くことにしたされていますC#で文字列データ表現を型に変換する

private static T ReadAttribute<T>(XElement Element,string AttributeName) 
    { 
     var attrib = Element.Attribute(AttributeName); 

     if (attrib != null) 
     { 
      return attrib.Value; // off cource error is in this line ! 
     } 
     else 
     { 
      return default(T); 
     } 
    } 

このメソッドは、指定された名前を持つ属性を読み取ろうとすべきであり、この属性は逃した場合、それは返す必要があります属性タイプのデフォルト値。属性の種類はTによって指定されます。 上記のコメントに示されているように、私の問題は文字列の値を特定の型に変換することができないことです。実際には、int型、double型、および2つの列挙型をTとして使用することを予定しています。
この状況ではどうすればよいですか?どのように私は文字列の値をT型に変換する必要がありますか?
ありがとうございます!

答えて

8

Convert.ChangeTypeを使用できます。それは基本的にあなたが望むものです。しかし、これはキャストではなく、キャストだけではなく、キャストではありません。

return (T)Convert.ChangeType(attrib.Value, typeof(T), CultureInfo.InvariantCulture); 

文字列を単純に任意の型にキャストできる理由は、型システムでは許容されないからです。しかし、Convert.ChangeTypeは任意の型である可能性のあるオブジェクトを返します。したがって、キャストは許可されます。

CultureInfo.InvariantCultureは、異なる文化を使用してXMLコンテンツをエンコード/デコードするべきではないため、重要です。 XmlConvertクラスはXMLで作業する場合に使用する必要がありますが、XmlConvert.ChangeTypeのような便利な汎用メソッドはありません。

XAttributeクラスには、XmlConvertクラスにマップされる多くの明示的なユーザー定義キャストがあります。ただし、これらを拘束のない型パラメータTで単純に使用することはできず、同じ結果が期待されます。

さらに悪いことに、XMLとConvertは実際にはうまくいきません。ですから、あなたが本当にこれについて深刻なことをしているなら、あなたはコンバージョンを処理するためにこのようなものを書くでしょう。

static T ConvertTo<T>(XAttribute attr) 
{ 
    object value; 
    switch (Type.GetTypeCode(typeof(T))) 
    { 
     case TypeCode.Boolean: value = XmlConvert.ToBoolean(attr.Value); break; 
     case TypeCode.Int32: value = XmlConvert.ToInt32(attr.Value); break; 
     case TypeCode.DateTime: value = XmlConvert.ToDateTime(attr.Value); break; 
     // Add support for additional TypeCode values here... 
     default: 
      throw new ArgumentException(string.Format("Unsupported destination type '{0}'.", typeof(T))); 
    } 
    return (T)value; 
} 
+0

Convert.ChangeTypeは、ソースが既に必要な型でない限り、IConvertibleを実装するためにソースオブジェクトを必要とします。 – sisve

+0

XAttribute(XmlConvertのメソッドを使用)によってフォワード変換が行われているが、逆変換はConvertで行われる危険性はありますか?私は、変換が異なるかもしれないDateTimeのようなコーナーケースがあるかどうか分かりません。 –

+0

はい!それは今のところ完璧に動作します。ありがとうございました!しかし、どうすればよいでしょうか?たとえば、私のメソッドを仮想クラスAで使用することはできません。そのためには、dintは文字列からの変換です。私が理解しているように、単に「どこでT:IConvertible」と指定する権利がないのであれば、十分な結果が得られません。 –

0

私はあなたのコードで、次のチェックを行うべきだと思う:Tは自分で定義された型であれば内蔵の方法

if (attrib.Value is T) 
{ 
... 
} 
+0

手伝ってくれて拒否してくれてありがとう! – Dummy01

+1

これは@ Dummy01のように動作します。あなたが間違っていると、間違った答えを出したくないので、あなたは投票を下すでしょう。しかし、もしあなたが誠実で、より多くを学ぶことを望まないなら、私はうまくそれを手伝ってくれるでしょう。あなたの提案は実際に型システムの規則を無視し、それは意味をなさない。 'Value'プロパティは' System.String'型で、 'T'が文字列でなければ' T'に対してテストをタイプしません。私はそれが問題を解決するわけではありませんし、あなたがその質問を理解していないことは明らかです。 –

+1

あなたは私に先生をするにはあまりにも多くの行を費やしました。私はあなたの直接の否定的な投票に完璧に合うと思います。しかし、ストーリーのために、そうです。私は間違って値がオブジェクトであり、文字列ではなく、それが私の小切手の出所であることを思い出していました。実際に、私が間違っていたと気づいた場合、あなたの問題に対する最も簡単な解決法であり、その点で私の小切手も使用します。しかし、私は先生から別の悪い投票をする危険はないので、自分でそれを理解しなければならないと思います。 – Dummy01

1

は助けにはなりません。のは、XMLがどのように見えるとしましょう:

//some other segments 
<Book Name="Good book" Price="20" Author="Jack" /> 

そして、あなたTが見えるクラスBookです:自動的BookのインスタンスへXElementを変換する魔法はありません

class Book 
{ 
    public string Name { get; set; } 
    public decimal Price { get; set; } 
    public string Author { get; set; } 
    //maybe some other properties 
} 

、必要に自分で実装してください。簡単で一般的な実装は次のようなものです:

interface IXElementConvertible 
    { 
     void LoadFrom(XElement element); 
    } 

    class Book : IXElementConvertible 
    { 
     public string Name { get; set; } 
     public decimal Price { get; set; } 
     public string Author { get; set; } 

     public void LoadFrom(XElement element) 
     { 
      this.Name = element.Attribute("Name").Value; 
      //blabla 
     } 
    } 

そして、あなたはあなたの方法に変更する必要があります。私はTypeConverterものとなるだろう

private static T ReadAttribute<T>(XElement Element,string AttributeName) 
          where T : IXElementConvertible, new() 
{ 
    T t = new T(); 
    t.LoadFrom(element); 
     //just an example here, not the complete implementation 
} 
5

を。それは基本的に、価値観や文化とのやりとりを行うクラスです。 TypeConverterとConvert.ChangeTypeの主な相違点は、TypeConvertersは任意のオブジェクトで動作することができますが、後でソースタイプでIConvertibleインターフェイスが必要になることです。

私はしばしばxmlファイルにさまざまな設定オブジェクトを保存するので、これのヘルパークラスを作成しました。これは、CultureInfo.InvariantCultureとの変換をハードコード化した理由です。

public static class TypeConversion { 
    public static Object Convert(Object source, Type targetType) { 
     var sourceType = source.GetType(); 
     if (targetType.IsAssignableFrom(sourceType)) 
      return source; 

     var sourceConverter = TypeDescriptor.GetConverter(source); 
     if (sourceConverter.CanConvertTo(targetType)) 
      return sourceConverter.ConvertTo(null, CultureInfo.InvariantCulture, source, targetType); 

     var targetConverter = TypeDescriptor.GetConverter(targetType); 
     if (targetConverter.CanConvertFrom(sourceType)) 
      return targetConverter.ConvertFrom(null, CultureInfo.InvariantCulture, source); 

     throw new ArgumentException("Neither the source nor the target has a TypeConverter that supports the requested conversion."); 
    } 


    public static TTarget Convert<TTarget>(object source) { 
     return (TTarget)Convert(source, typeof(TTarget)); 
    } 
} 

それは実際のVersionオブジェクトにバージョン番号(「ABCD」)を含む文字列から同様の変換をサポートするために、(IConvertibleを実装doesntの)System.Versionのように、システムタイプを処理するために、独自のTypeConverterを作成するために完全に可能です。

public class VersionTypeConverter : TypeConverter { 
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { 
     if (sourceType == typeof(string)) 
      return true; 

     return base.CanConvertFrom(context, sourceType); 
    } 

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { 
     var s = value as string; 
     if (s != null) 
      return new Version(s); 

     return base.ConvertFrom(context, culture, value); 
    } 

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { 
     if (destinationType == typeof(string)) 
      return true; 

     return base.CanConvertTo(context, destinationType); 
    } 

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { 
     var v = value as Version; 
     if (v != null && destinationType == typeof(string)) { 
      return v.ToString(); 
     } 

     return base.ConvertTo(context, culture, value, destinationType); 
    } 
} 

実際にカスタムTypeDescriptionProvider、およびtypeof(Version)に渡し、TypeDescriptor.AddProviderを使用して、アプリケーションの起動時に、それを登録する必要があり、このプロバイダーを使用します。これはTypeDescriptorProvider.GetTypeDescriptorメソッドでカスタムCustomTypeDescriptorを返す必要があり、この記述子はGetConverterをオーバーライドしてVersionTypeConverterの新しいインスタンスを返す必要があります。簡単です。 ;)

+0

うわー!それは本当に強力なソリューションです。今私のプログラマーでそれを使用しようとしています。ありがとうございました! –

+0

私はIConvertibleを実装しているすべてのシステムタイプにTypeConverterも既にあると思います。それが非常にコードが必要な主な理由は、TypeDescriptorのものがリフレクションフレームワーク全体をラップすることです。 CustomTypeDescriptor.GetPropertiesをオーバーライドしてプロパティを追加/削除し、TypeDescriptor.GetPropertiesを使用するユーザーには新しいものが表示されます。必要に応じて独自のプロパティを作成し、自分のストレージを処理できます。 – sisve

+0

@SimonSvensson非常に便利で、聖なる古いバットマンの投稿ですが、ConvertToを試す前に試してみる理由があります。もしそうなら、あなたの*(今3歳)*回答に追加できますか?私の使用事例では、データベースからint型、文字列型などを既知のカスタム型にマップするカスタムORMを使用しているため、ConvertToよりConvertToを頻繁に使用する必要があると考えています。 int converter.CanConvertTo()は常にfalseを返し、書き込みは書き込みよりも頻繁に起こるので、何かが見つからない限り、その順序を反転します*(カスタムORMを書いたり使用したりする以外は)* – Damon

関連する問題