Преобразовать в Nullable <T>из строки, используя Reflection - PullRequest
3 голосов
/ 21 мая 2009

Как я могу преобразовать в Nullable из строки, используя отражение?

У меня есть следующий код для преобразования в почти любой тип значения, заданный практически для любого значения. Над этим довольно много кода для использования IsAssignableFrom и т. Д., Так что это последнее средство.

MethodInfo parse = t.GetMethod("Parse", new Type[] { typeof(string) });

if (parse != null)
{
    object parsed = parse.Invoke(null, new object[] { value.ToString() });
    return (T)parsed;
}
else
{
    throw new InvalidOperationException("The value you specified is not a valid " + typeof(T).ToString());
}

Проблема возникает, когда я хочу преобразовать в обнуляемый тип, например long?.

Очевидно, долго? класс не имеет метода разбора.

Как извлечь метод синтаксического анализа из шаблона типа nullable?

EDIT:

Вот небольшая батарея тестов, которые я пытаюсь пройти:

[Test]
public void ConverterTNullable()
{
    Assert.That((int?)1, Is.EqualTo(Converter<int?>.Convert(1)));
    Assert.That((int?)2, Is.EqualTo(Converter<int?>.Convert(2.0d)));
    Assert.That(3, Is.EqualTo(Converter<long>.Convert(3)));

    Assert.That((object)null, Is.EqualTo(Converter<long?>.Convert("")));
    Assert.That((object)null, Is.EqualTo(Converter<long?>.Convert(null)));
    Assert.That((object)null, Is.EqualTo(Converter<long?>.Convert(DBNull.Value)));

    Assert.That((long)1, Is.EqualTo(Converter<long?>.Convert("1")));
    Assert.That((long)2, Is.EqualTo(Converter<long?>.Convert(2.0)));
    Assert.That((long?)3, Is.EqualTo(Converter<long>.Convert(3)));
}

И вся функция:

/// <summary>
/// Converts any compatible object to an instance of T.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>The converted value.</returns>
public static T Convert(object value)
{
    if (value is T)
    {
        return (T)value;
    }

    Type t = typeof(T);

    if (t == typeof(string))
    {
        if (value is DBNull || value == null)
        {
            return (T)(object)null;
        }
        else
        {
            return (T)(object)(value.ToString());
        }
    }
    else
    {
        if (value is DBNull || value == null)
        {
            return default(T);
        }

        if (value is string && string.IsNullOrEmpty((string)value))
        {
            return default(T);
        }

        try
        {
            return (T)value;
        }
        catch (InvalidCastException)
        {
        }

        if (Nullable.GetUnderlyingType(t) != null)
        {
            t = Nullable.GetUnderlyingType(t);
        }

        MethodInfo parse = t.GetMethod("Parse", new Type[] { typeof(string) });

        if (parse != null)
        {
            object parsed = parse.Invoke(null, new object[] { value.ToString() });
            return (T)parsed;
        }
        else
        {
            throw new InvalidOperationException("The value you specified is not a valid " + typeof(T).ToString());
        }
    }
}

Ответы [ 3 ]

7 голосов
/ 21 мая 2009

Я бы, вероятно, использовал TypeConverter в этом случае и Nullable.GetUnderlyingType(); пример в пути ...

    static void Main()
    {
        long? val1 = Parse<long?>("123");
        long? val2 = Parse<long?>(null);
    }

    static T Parse<T>(string value)
    {
        return (T) TypeDescriptor.GetConverter(typeof(T))
            .ConvertFrom(value);
    }
2 голосов
/ 21 мая 2009

Это должно к работе. (Код для простоты встроен в метод расширения, хотя он может быть не тем, что вы хотите.)

public static T? ParseToNullable<T>(this string value) where T : struct
{
    var parseMethod = typeof(T).GetMethod("Parse", new Type[] { typeof(string) });
    if (parseMethod == null)
        return new Nullable<T>();

    try
    {
        var value = parseMethod.Invoke(null, new object[] { value.ToString() });
        return new Nullable<T>((T)value);
    }
    catch
    {
        return new Nullable<T>();
    }
}

Теперь, если вы хотите, чтобы параметр общего типа сам был типом, допускающим обнуляемый тип, а не базовым (я не вижу в этом преимущества), вы можете использовать: *

Nullable.GetUnderlyingType(typeof(T))

, как предложил Марк Гравелл, и для этого потребуется всего несколько незначительных изменений в коде.

0 голосов
/ 21 мая 2009

Я добавил это немного:

if (Nullable.GetUnderlyingType(t) != null)
{
    t = Nullable.GetUnderlyingType(t);
}

MethodInfo parse = t.GetMethod("Parse", new Type[] { typeof(string) });

Спасибо всем!

...