Преобразование универсального типа из строки

У меня есть класс, который я хочу использовать для хранения «свойств» другого класса. У этих свойств просто есть имя и значение. В идеале я хотел бы иметь возможность добавлять типизированные свойства, чтобы возвращаемое "значение" всегда было того типа, который мне нужен.

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

Итак, я создал класс, который (примерно) таков:

public class TypedProperty<DataType> : Property
{
    public DataType TypedValue
    {
        get { // Having problems here! }
        set { base.Value = value.ToString();}
    }
}

Итак, вопрос:

Есть ли «общий» способ преобразования строки обратно в примитив?

Кажется, я не могу найти какой-либо общий интерфейс, который связывает преобразование по всем направлениям (что-то вроде ITryParsable было бы идеальным!).


person Rob Cooper    schedule 12.08.2008    source источник
comment
Мне было бы интересно увидеть пример вашего конкретного класса, даже просто фрагмент. :)   -  person Jon Limjap    schedule 12.08.2008
comment
не могли бы вы опубликовать соответствующие части вашего базового класса?   -  person JJS    schedule 12.07.2012
comment
Интересно, сможет ли кто-нибудь получить здесь ответы, работая в .Net Standard 1.2: /   -  person Dan Rayson    schedule 03.11.2018


Ответы (11)


Не уверен, правильно ли я понял ваши намерения, но давайте посмотрим, поможет ли это.

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get { return (T)Convert.ChangeType(base.Value, typeof(T)); }
        set { base.Value = value.ToString();}
    }
}
person lubos hasko    schedule 12.08.2008
comment
В течение нескольких дней я задавался вопросом, как десериализовать поток в общий тип. Спасибо :) - person Trap; 01.10.2008
comment
Я согласен, хотя Convert.ChangeType не очень универсальное и расширяемое решение, оно работает для большинства основных типов. если нужно что-то лучшее, не проблема превратить этот метод во что-то большее, как предложил Тим, или использовать другой метод преобразования в целом. - person lubos hasko; 11.01.2010
comment
Я бы определенно добавил where T: IConvertible - person MikeT; 07.11.2013
comment
Тип T не должен быть IConvertible, а должен быть Type of base.Value. - person chapluck; 15.09.2015
comment
Похоже, это не работает для GUID. - person jakubiszon; 15.04.2021

Метод lubos hasko не работает для значений NULL. Приведенный ниже метод будет работать для значений NULL. Однако я не придумал этого. Нашел через Google: http://web.archive.org/web/20101214042641/http://dogaoztuzun.com/post/C-Generic-Type-Conversion.aspx Кредит "Тунец Токсоз"

Сначала использование:

TConverter.ChangeType<T>(StringValue);  

Класс ниже.

public static class TConverter
{
    public static T ChangeType<T>(object value)
    {
        return (T)ChangeType(typeof(T), value);
    }

    public static object ChangeType(Type t, object value)
    {
        TypeConverter tc = TypeDescriptor.GetConverter(t);
        return tc.ConvertFrom(value);
    }

    public static void RegisterTypeConverter<T, TC>() where TC : TypeConverter
    {

        TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC)));
    }
}
person Tim Coker    schedule 02.12.2009
comment
Я бы добавил альтернативные варианты преобразования для перечислений и других сложных структур, но хороший вызов. - person Tomer W; 27.03.2012
comment
Почему RegisterTypeConverter? Нужно ли регистрировать конвертеры заранее? (к сожалению, ссылка мертва, поэтому я не смог ее прочитать) - person Cohen; 30.11.2012
comment
Для множественных преобразований вам, вероятно, следует создать tc (TypeConverter) только один раз. TypeConverter работает медленно, потому что использует отражение для поиска TypeConverterAttribute. Если вы инициализируете одно частное поле TypeConverter, вы сможете повторно использовать TypeConverter много раз. - person Kevin P. Rice; 21.01.2013
comment
Работает нормально, но если T - object, выдает исключение. Мне удалось обойти это, используя `if (typeof (T) .IsPrimitive) {return TConverter.ChangeType ‹T› (StringValue); } else {объект o = (объект) StringValue; Вернуться к; } `вместо примера использования TConverter.ChangeType<T>(StringValue) - person Matt; 24.02.2016

Для многих типов (integer, double, DateTime и т. Д.) Существует статический метод Parse. Вы можете вызвать его с помощью отражения:

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

if (m != null)
{
    return m.Invoke(null, new object[] { base.Value });
}
person dbkk    schedule 12.08.2008

TypeDescriptor.GetConverter(PropertyObject).ConvertFrom(Value)

TypeDescriptor - это класс, имеющий метод GetConvertor, который принимает объект Type, а затем вы можете вызвать метод ConvertFrom для преобразования value для этого указанного объекта.

person Dinesh Rathee    schedule 09.07.2013
comment
Я лично считаю, что этот интерфейс лучше для обработки преобразования, чем метод Convert.ChangeType, поскольку вам необходимо реализовать интерфейс IConvertible для всего вашего класса. - person Sauleil; 29.11.2019

Вдохновленные ответом Боба, эти расширения также поддерживают значение null преобразование значения и все примитивное преобразование назад и четвертое.

public static class ConversionExtensions
{
        public static object Convert(this object value, Type t)
        {
            Type underlyingType = Nullable.GetUnderlyingType(t);

            if (underlyingType != null && value == null)
            {
                return null;
            }
            Type basetype = underlyingType == null ? t : underlyingType;
            return System.Convert.ChangeType(value, basetype);
        }

        public static T Convert<T>(this object value)
        {
            return (T)value.Convert(typeof(T));
        }
}

Примеры

            string stringValue = null;
            int? intResult = stringValue.Convert<int?>();

            int? intValue = null;
            var strResult = intValue.Convert<string>();
person Ghominejad    schedule 13.03.2019

Вы могли бы использовать такую ​​конструкцию, как класс черт. Таким образом, у вас будет параметризованный вспомогательный класс, который знает, как преобразовать строку в значение ее собственного типа. Тогда ваш геттер может выглядеть так:

get { return StringConverter<DataType>.FromString(base.Value); }

Теперь я должен отметить, что мой опыт работы с параметризованными типами ограничен C ++ и его шаблонами, но я полагаю, что есть какой-то способ сделать то же самое с помощью универсальных шаблонов C #.

person Greg Hewgill    schedule 12.08.2008
comment
Универсальных версий в C # не существует. - person Shimmy Weitzhandler; 10.07.2015

Проверьте статический Nullable.GetUnderlyingType. - Если базовый тип имеет значение NULL, то параметр шаблона не Nullable, и мы можем использовать этот тип напрямую. - Если базовый тип не равен NULL, тогда используйте базовый тип в преобразовании.

Кажется, у меня работает:

public object Get( string _toparse, Type _t )
{
    // Test for Nullable<T> and return the base type instead:
    Type undertype = Nullable.GetUnderlyingType(_t);
    Type basetype = undertype == null ? _t : undertype;
    return Convert.ChangeType(_toparse, basetype);
}

public T Get<T>(string _key)
{
    return (T)Get(_key, typeof(T));
}

public void test()
{
    int x = Get<int>("14");
    int? nx = Get<Nullable<int>>("14");
}
person Bob C    schedule 07.08.2015

public class TypedProperty<T> : Property
{
    public T TypedValue
    {
        get { return (T)(object)base.Value; }
        set { base.Value = value.ToString();}
    }
}

Я использую преобразование через объект. Это немного проще.

person Mastahh    schedule 17.12.2010
comment
спасибо, мне нужно преобразовать T в интерфейсе, и простой объект преобразования T работает правильно, легко и быстро, спасибо - person LXG; 10.02.2012
comment
(int) (объект) 54; это FATALITY !, это не VB! - person Tomer W; 27.03.2012

Я использовал ответ лобоса, и он работает. Но у меня возникла проблема с преобразованием двойных из-за настроек культуры. Я добавил

return (T)Convert.ChangeType(base.Value, typeof(T), CultureInfo.InvariantCulture);
person anhoppe    schedule 03.06.2015

Еще одна вариация. Обрабатывает значения Nullables, а также ситуации, когда строка имеет значение NULL, а T не допускает значение NULL.

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get
        {
            if (base.Value == null) return default(T);
            var type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
            return (T)Convert.ChangeType(base.Value, type);
        }
        set { base.Value = value.ToString(); }
    }
}
person Todd Menier    schedule 07.08.2017

Вы можете сделать это одной строкой, как показано ниже:

YourClass obj = (YourClass)Convert.ChangeType(YourValue, typeof(YourClass));

Удачного кодирования;)

person Hemendra    schedule 06.03.2020
comment
Если вы поделились кодом в качестве ответа, попробуйте его объяснить. - person Yunus Temurlenk; 06.03.2020