2011-04-22 2 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로 지정됩니다. 위의 주석에서 알 수 있듯이 문자열 값을 특정 유형으로 변환 할 수 없습니다. 사실 저는 T로 int, double 및 two enum 유형을 사용할 계획입니다.
이 상황에서 어떻게해야합니까? 어떻게 문자열 값을 T 타입으로 변환해야합니까?
미리 감사드립니다.

답변

8

Convert.ChangeType을 사용할 수 있습니다. 그것은 기본적으로 당신이 원하는 것을합니다. 그러나 이것은 캐스팅이 아니라 캐스팅이 아니라 캐스팅이 아닙니다.

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

문자열을 임의의 유형으로 단순히 캐스팅 할 수있는 이유는 유형 시스템에서이를 허용하지 않기 때문입니다. 그러나 Convert.ChangeType은 모든 유형이 될 수있는 객체를 반환하므로 캐스트가 허용됩니다.

CultureInfo.InvariantCulture은 중요한 콘텐츠입니다. 왜냐하면 XML 콘텐츠가 다른 문화권을 사용하여 인코딩/디코딩되지 않아야하기 때문입니다. XML을 사용하는 경우 XmlConvert 클래스를 사용해야하지만 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의 메서드를 사용하여) 정방향 변환이 수행되었지만 변환으로 변환이 역전 될 위험이 있습니까? 예를 들어 DateTime과 같은 변환이 다를 수있는 코너 케이스가 있는지 나는 알지 못합니다. –

+0

예! 그것은 지금 당장 완벽하게 작동합니다. 고맙습니다! 그러나 예를 들어, 가상의 classA로 내 메서드를 사용할 수 없다는 것을 어떻게 확신 할 수 있는가? 왜냐하면 dint는 문자열에서의 변환이 존재하기 때문이다. 내가'T : IConvertible'을 단순히 지정하는 것이 옳지 않다는 것을 이해함에 따라 충분한 결과가 나오지 않을 것입니다. –

0

나는 당신이 코드에서 다음과 같은 검사를 수행해야한다고 생각 : T 자신에 의해 정의 된 유형 인 경우

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

도와 주려고 할 때 부정적인 투표에 감사드립니다 !!! – Dummy01

+1

그것은 @ Dummy01처럼 작동합니다. 틀린 대답을 홍보하고 싶지 않기 때문에 틀린 투표를하면됩니다. 그러나 당신이 진실하고 더 배우기를 원하지 않는다면, 나는 당신을 행복하게 도와 줄 것입니다. 귀하의 제안은 실제로 형식 시스템의 규칙을 무시하고 이해가되지 않습니다. 'Value' 프로퍼티는'System.String' 타입입니다.'T'가 문자열이 아니라면'T'에 대해서 타입 테스트를하지 않습니다. 나는 그 일의 요지를 보지 못하고 문제를 해결하지 못한다. 그리고 그 질문을 이해하지 못했다는 것이 나에게 분명하다. –

+1

선생님을 저에게 너무 많이 보냈습니다. 나는 당신의 직접적인 부정 투표에 완벽하다고 생각합니다. 그러나이 이야기에 대해 네 말이 맞습니다. Value가 객체이고 문자열이 아니라는 것을 잘못 기억했습니다. 이것이 내 수표의 출처입니다. 사실 내가 방금 내가 잘못했다고 말한 것을 눈치 채면 문제에 대한 가장 쉬운 해결책이며 내 수표를 사용할 것입니다. 그러나 나는 선생님의 또 다른 나쁜 표를받을 위험이 없으므로 혼자서 알아 내야한다고 생각합니다. – Dummy01

1

내장 방법이 도움이되지 않습니다. 당신 T 모습 클래스 책이다

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

그리고 :의이 같은 XML 보이는 가정 해 봅시다

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

자동 Book의 인스턴스에 XElement을 변환하는 마법이 없다, 당신은 필요 직접 구현하십시오. 쉬운 및 일반 구현은 다음과 같이이다 :

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 
     } 
    } 

그리고 당신은 당신의 방법을 수정해야합니다

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 물건 갈 것입니다. 그것은 기본적으로 가치와 문화 간의 전환을하는 클래스입니다. TypeConverter와 Convert.ChangeType의 주된 차이점은 소스 형식에서 IConvertible 인터페이스가 필요하고 TypeConverters는 모든 개체에서 작동 할 수 있다는 것입니다.

나는 종종 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)); 
    } 
} 

이 System.Version 같은 시스템 유형을, (IConvertible을 구현 나던하는) 실제 버전 개체에 버전 번호 ("ABCD")를 포함하는 문자열에서 같은 변환을 지원하기 위해 처리하기 위해 자신의 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); 
    } 
} 

실제로 사용자 정의 TypeDescriptionProvidertypeof(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보다 ConvertFrom이 더 자주 필요하다고 생각합니다. int converter.CanConvertTo()는 항상 false를 반환하고 쓰기는 쓰기보다 훨씬 빈번하게 발생하므로 뭔가를 놓치지 않는 한 그 순서를 바꿀 것입니다. (사용자 지정 ORM을 쓰거나 사용하지 않는 것 이외의 다른 것) * – Damon

관련 문제