2010-10-07 12 views
6

Estoy tratando de escribir un método genérico para obtener un valor XElement de una manera fuertemente tipada. Aquí es lo que tengo:Cómo convertir a parámetro genérico en C#?

public static class XElementExtensions 
{ 
    public static XElement GetElement(this XElement xElement, string elementName) 
    { 
     // Calls xElement.Element(elementName) and returns that xElement (with some validation). 
    } 

    public static TElementType GetElementValue<TElementType>(this XElement xElement, string elementName) 
    { 
     XElement element = GetElement(xElement, elementName); 
     try 
     { 
      return (TElementType)((object) element.Value); // First attempt. 
     } 
     catch (InvalidCastException originalException) 
     { 
      string exceptionMessage = string.Format("Cannot cast element value '{0}' to type '{1}'.", element.Value, 
       typeof(TElementType).Name); 
      throw new InvalidCastException(exceptionMessage, originalException); 
     } 
    } 
} 

Como se puede ver en la línea de GetElementValueFirst attempt, estoy tratando de pasar de la cadena -> objeto -> TElementType. Desafortunadamente, esto no funciona para un caso de prueba entero. Cuando se ejecuta la siguiente prueba:

[Test] 
public void GetElementValueShouldReturnValueOfIntegerElementAsInteger() 
{ 
    const int expectedValue = 5; 
    const string elementName = "intProp"; 
    var xElement = new XElement("name"); 
    var integerElement = new XElement(elementName) { Value = expectedValue.ToString() }; 
    xElement.Add(integerElement); 

    int value = XElementExtensions.GetElementValue<int>(xElement, elementName); 

    Assert.AreEqual(expectedValue, value, "Expected integer value was not returned from element."); 
} 

me sale el siguiente excepción cuando GetElementValue<int> se llama:

System.InvalidCastException : Cannot cast element value '5' to type 'Int32'.

¿Voy a tener que manejar cada caso fundición (o al menos los numéricos) por separado?

Respuesta

11

También puede probar la cadena Convert.ChangeType

Convert.ChangeType(element.Value, typeof(TElementType)) 
+0

Esto funciona para mí. 'ChangeType' devuelve un objeto fijo, pero el molde implícito ahora funciona. Además, ahora compruebo para 'FormatException' en lugar de' InvalidCastException' en el bloque 'try/catch'. Respuesta corta y simple. – Scott

+0

Tras una investigación adicional, no puede usar esto con tipos anulables. Entonces, usando este artículo: http://aspalliance.com/852 escribí un método de extensión para manejar ambos tipos. – Scott

1

En C# no puede lanzar objeto de cadena a Int32. Por ejemplo, este código produce error de compilación:

string a = "123.4"; 
    int x = (int) a; 

Intente utilizar Convert class o Int32.Parse y Int32.TryParse métodos si desea que dicha funcionalidad.
Y sí, debe manejar la conversión numérica por separado, si desea convertir la cadena a int.

2

No se puede hacer una conversión implícita o explícita String-Int32, es necesario utilizar Int32 's Parse o TryParse métodos para ello. Probablemente pueda crear algunos métodos de extensión ingeniosos, por ejemplo:

using System; 
    using System.Diagnostics; 
    using System.Globalization; 
    using System.Text; 

    /// <summary> 
    /// Provides extension methods for strings. 
    /// </summary> 
    public static class StringExtensions 
    { 
     #region Methods 
     /// <summary> 
     /// Converts the specified string to a <see cref="Boolean"/> 
     /// </summary> 
     /// <param name="string">The string to convert.</param> 
     /// <returns>The specified string as a <see cref="Boolean"/>.</returns> 
     public static bool AsBoolean(this string @string) 
     { 
      return bool.Parse(@string); 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="Boolean"/> using TryParse. 
     /// </summary> 
     /// <remarks> 
     /// If the specified string cannot be parsed, the default value (if valid) or false is returned. 
     /// </remarks> 
     /// <param name="string">The string to convert.</param> 
     /// <param name="default">The default value for if the value cannot be parsed.</param> 
     /// <returns>The specified string as a <see cref="DateTime"/>.</returns> 
     public static bool AsBooleanNonStrict(this string @string, bool? @default = null) 
     { 
      bool @bool; 
      if ((!string.IsNullOrEmpty(@string)) && bool.TryParse(@string, out @bool)) 
       return @bool; 

      if (@default.HasValue) 
       return @default.Value; 

      return false; 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="DateTime"/> 
     /// </summary> 
     /// <param name="string">The string to convert.</param> 
     /// <returns>The specified string as a <see cref="DateTime"/>.</returns> 
     public static DateTime AsDateTime(this string @string) 
     { 
      return DateTime.Parse(@string); 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="DateTime"/> using TryParse. 
     /// </summary> 
     /// <remarks> 
     /// If the specified string cannot be parsed, <see cref="DateTime.MinValue"/> is returned. 
     /// </remarks> 
     /// <param name="string">The string to convert.</param> 
     /// <param name="default">The default value for if the value cannot be parsed.</param> 
     /// <returns>The specified string as a <see cref="DateTime"/>.</returns> 
     public static DateTime AsDateTimeNonStrict(this string @string, DateTime? @default = null) 
     { 
      DateTime datetime; 
      if ((!string.IsNullOrEmpty(@string)) && DateTime.TryParse(@string, out datetime)) 
       return datetime; 

      if (@default.HasValue) 
       return @default.Value; 

      return DateTime.MinValue; 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="TEnum"/> 
     /// </summary> 
     /// <param name="string">The string to convert.</param> 
     /// <returns>The specified string as a <see cref="TEnum"/>.</returns> 
     public static TEnum AsEnum<TEnum>(this string @string) where TEnum : struct 
     { 
      return (TEnum)Enum.Parse(typeof(TEnum), @string); 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="TEnum"/> 
     /// </summary> 
     /// <param name="string">The string to convert.</param> 
     /// <returns>The specified string as a <see cref="TEnum"/>.</returns> 
     public static TEnum AsEnumNonStrict<TEnum>(this string @string, TEnum @default) where TEnum : struct 
     { 
      TEnum @enum; 
      if ((!string.IsNullOrEmpty(@string)) && Enum.TryParse(@string, out @enum)) 
       return @enum; 

      return @default; 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="Int32"/> 
     /// </summary> 
     /// <param name="string">The string to convert.</param> 
     /// <returns>The specified string as a <see cref="Int32"/>.</returns> 
     public static int AsInteger(this string @string) 
     { 
      return int.Parse(@string); 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="Int32"/> using TryParse. 
     /// </summary> 
     /// <remarks> 
     /// If the specified string cannot be parsed, the default value (if valid) or 0 is returned. 
     /// </remarks> 
     /// <param name="string">The string to convert.</param> 
     /// <param name="default">The default value for if the value cannot be parsed.</param> 
     /// <returns>The specified string as a <see cref="Int32"/>.</returns> 
     public static int AsIntegerNonStrict(this string @string, int? @default = null) 
     { 
      int @int; 
      if ((!string.IsNullOrEmpty(@string)) && int.TryParse(@string, out @int)) 
       return @int; 

      if (@default.HasValue) 
       return @default.Value; 

      return 0; 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="bool"/> 
     /// </summary> 
     /// <param name="string">The string to convert.</param> 
     /// <returns>The specified string as a <see cref="DateTime"/>.</returns> 
     public static bool? AsNullableBolean(this string @string) 
     { 
      bool @bool; 
      if ((string.IsNullOrEmpty(@string)) || !bool.TryParse(@string, out @bool)) 
       return null; 

      return @bool; 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="DateTime"/> 
     /// </summary> 
     /// <param name="string">The string to convert.</param> 
     /// <returns>The specified string as a <see cref="DateTime"/>.</returns> 
     public static DateTime? AsNullableDateTime(this string @string) 
     { 
      DateTime dateTime; 
      if ((string.IsNullOrEmpty(@string)) || !DateTime.TryParse(@string, out dateTime)) 
       return null; 

      return dateTime; 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="DateTime"/> 
     /// </summary> 
     /// <param name="string">The string to convert.</param> 
     /// <returns>The specified string as a <see cref="DateTime"/>.</returns> 
     public static TEnum? AsNullableEnum<TEnum>(this string @string) where TEnum : struct 
     { 
      TEnum @enum; 
      if ((string.IsNullOrEmpty(@string)) || !Enum.TryParse(@string, out @enum)) 
       return null; 

      return @enum; 
     } 

     /// <summary> 
     /// Converts the specified string to a <see cref="Int32"/> 
     /// </summary> 
     /// <param name="string">The string to convert.</param> 
     /// <returns>The specified string as a <see cref="Int32"/>.</returns> 
     public static int? AsNullableInteger(this string @string) 
     { 
      int @int; 
      if ((string.IsNullOrEmpty(@string)) || !int.TryParse(@string, out @int)) 
       return null; 

      return @int; 
     } 
     #endregion 
    } 

Normalmente los uso bastante a menudo.

0

también implementa IConvertible, por lo que puede hacer lo siguiente.

((IConvertible)mystring).ToInt32(null); 

Incluso puede lanzar algunos métodos de extensión para que quede más limpio.

3

Desde su código, en lugar de:

return (TElementType)((object) element.Value); 

que haría esto:

return (TElementType) Convert.ChangeType(element.Value, typeof (T)); 

La única salvedad es que TElementType debe implementar IConvertible. Sin embargo, si solo está hablando de tipos intrínsecos, todos implementan eso ya.

Para sus tipos personalizados, suponiendo que los quisiera aquí, tendría que tener su propia conversión.

Cuestiones relacionadas