Параметры общего типа, использующие out - PullRequest
5 голосов
/ 29 апреля 2010

Я пытаюсь сделать универсальный парсер, использующий параметры общего типа, но я не могу понять концепцию 100%

    private bool TryParse<T>(XElement element, string attributeName, out T value) where T : struct
    {
        if (element.Attribute(attributeName) != null && !string.IsNullOrEmpty(element.Attribute(attributeName).Value))
        {
            string valueString = element.Attribute(attributeName).Value;
            if (typeof(T) == typeof(int))
            {
                int valueInt;
                if (int.TryParse(valueString, out valueInt))
                {
                    value = valueInt;
                    return true;
                }
            }
            else if (typeof(T) == typeof(bool))
            {
                bool valueBool;
                if (bool.TryParse(valueString, out valueBool))
                {
                    value = valueBool;
                    return true;
                }
            }
            else
            {
                value = valueString;
                return true;
            }
        }

        return false;
    }

Как вы можете догадаться, код не компилируется, так как я не могу преобразовать int | bool | string в T (например, value = valueInt). Благодарен за обратную связь, возможно, даже так, как я это делаю. Использование .NET 3.5

Ответы [ 8 ]

4 голосов
/ 29 апреля 2010

Оба класса XElement и XAttribute предоставляют набор явных операторов преобразования (приведение) для удобного преобразования их содержимого в примитивные типы .NET.

Например, вы можете просто сделать:

XElement elem = // ...

string value1 = (string)elem.Attribute("myString");
int    value2 = (int)elem.Attribute("myInt");
int?   value3 = (int?)elem.Attribute("myOptionalInt");
bool   value4 = (bool)elem.Attribute("myBool");
2 голосов
/ 29 апреля 2010

Учитывая, что вы пишете только большую комбинацию if / then, я думаю, вам было бы лучше просто с кучей перегрузок:

public static class Parser
{
    private static string TryParseCommon(XElement element, string attributeName)
    {
        if (element.Attribute(attributeName) != null && !string.IsNullOrEmpty(element.Attribute(attributeName).Value))
        {
            return element.Attribute(attributeName).Value;
        }

        return null;
    }

    public static bool TryParse(XElement element, string attributeName, out string value)
    {
        value = TryParseCommon(element, attributeName);
        return true;
    }

    public static bool TryParse(XElement element, string attributeName, out int value)
    {
        return int.TryParse(TryParseCommon(element, attributeName), out value);
    }

    public static bool TryParse(XElement element, string attributeName, out bool value)
    {
        return bool.TryParse(TryParseCommon(element, attributeName), out value);
    }
}
2 голосов
/ 29 апреля 2010

Не самая лучшая вещь, но вы можете привести ваш T к чему-то другому, если вы выполните цикл над объектом, то есть сначала приведете к объекту, затем к T или наоборот. Я ничего не говорю о том, занимаетесь ли вы боксом / распаковкой, но компилятор примет это.

1 голос
/ 29 апреля 2010

Этот метод, который я использовал в прошлом, тоже может помочь.

public static T ChangeTypeTo<T>(this object value)
{
    if (value == null)
        return null;

    Type underlyingType = typeof (T);
    if (underlyingType == null)
        throw new ArgumentNullException("value");

    if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition()
                                            .Equals(typeof (Nullable<>)))
    {
        var converter = new NullableConverter(underlyingType);
        underlyingType = converter.UnderlyingType;
    }

    // Guid convert
    if (underlyingType == typeof (Guid))
    {
        return new Guid(value.ToString());
    }

    // Check for straight conversion or value.ToString conversion
    var objType = value.GetType();

    // If this is false, lets hope value.ToString can convert otherwise exception
    bool objTypeAssignable2typeT = underlyingType.IsAssignableFrom(objType);

    // Do conversion
    return (T) (objTypeAssignable2typeT ? 
              Convert.ChangeType(value, underlyingType)
            : Convert.ChangeType(value.ToString(), underlyingType));
}
1 голос
/ 29 апреля 2010

Проблема в том, что вы пытаетесь сделать что-то общее , которое на самом деле не так. Даже если вы вручную просматриваете все типы, которые можете анализировать сами, это не учитывает все типы.

Почему бы тебе не попробовать это вместо этого? Сначала определите делегата, который описывает общий TryParse метод:

public delegate bool TryParser<T>(string text, out T value);

Затем реструктурируйте ваш метод, чтобы взять один из них в качестве параметра:

// uglified code to fit within horizontal scroll area
public bool TryParse<T>
(XElement element, string attributeName, TryParser<T> tryParser, out T value)
{
    value = default(T);

    if (
        element.Attribute(attributeName) != null &&
        !string.IsNullOrEmpty(element.Attribute(attributeName).Value)
    )
    {
        string valueString = element.Attribute(attributeName).Value;
        return tryParser(valueString, out value);
    }

    return false;
}

Теперь перегрузить это для всех стандартных типов довольно тривиально:

public bool TryParseInt(XElement element, string attributeName, out int value)
{
    return TryParse<int>(element, attributeName, int.TryParse, out value);
}

public bool TryParseBool(XElement element, string attributeName, out bool value)
{
    return TryParse<bool>(element, attributeName, bool.TryParse, out value);
}

и т. Д.

Что хорошо в этом подходе, так это то, что он даже не ограничивает вас использованием ограничения where T : struct или даже встроенными типами .NET. Пользователь этого класса синтаксического анализатора может анализировать свои собственные типы из XML-документа, просто определяя TryParse -подобный метод для своих пользовательских типов.

1 голос
/ 29 апреля 2010
1 голос
/ 29 апреля 2010

Это может сработать для вас.

private bool TryParse<T>(XElement element, string attributeName,out T value)
{
    if (element.Attribute(attributeName) != null && !string.IsNullOrEmpty(element.Attribute(attributeName).Value))
    {
        string valueString = element.Attribute(attributeName).Value;
        TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
        try
        {
            value = (T)converter.ConvertFrom(valueString);
            return true;
        }
        catch
        {
            value = default(T);
            return false;
        }
    }
    value = default(T);
    return false;
}
1 голос
/ 29 апреля 2010

Я делал методы Parse перед использованием TypeConverter.

TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
try
{
    return (T)converter.ConvertFrom(value);
}
...