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


234

У мене є клас, який я хочу використовувати для зберігання "властивостей" для іншого класу. Ці властивості просто мають ім'я та значення. В ідеалі я хотів би мати можливість додати введені властивості, щоб повернене "значення" завжди було такого типу, яким я його хочу.

Тип завжди повинен бути примітивним. Цей клас підкласи - абстрактний клас, який в основному зберігає ім'я та значення як рядок. Ідея полягає в тому, що цей підклас додасть базовий клас певної безпеки (а також заощадить мене на деякій конверсії).

Отже, я створив клас, який (приблизно) такий:

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

Тож питання:

Чи існує "загальний" спосіб перетворення з рядка назад у примітив?

Я, здається, не можу знайти жодного загального інтерфейсу, який би пов'язував конвертацію через дошку (щось на зразок ITryParable було б ідеально!).


Мені було б цікаво побачити приклад вашого конкретного класу, навіть просто фрагмент. :)
Джон Лім’яп

чи можете ви опублікувати відповідні частини вашого базового класу?
JJS

Цікаво, чи зможе хтось отримати відповіді тут, працюючи в .Net Standard 1.2: /
Dan Rayson

Відповіді:


374

Я не впевнений, чи правильно я зрозумів ваші наміри, але давайте подивимось, чи допомагає цей.

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();}
    }
}

Я декілька днів задавався питанням, як дезаріалізувати потік у загальний тип. Дякую :)
Пастка

3
Я погоджуюся, хоча Convert.ChangeTypeце не дуже універсальне та розширне рішення, але воно працює для більшості основних типів. якщо потрібне щось краще, це не проблема перетворити цей метод на щось більше, як запропонував Тім, або взагалі використовувати інший метод перетворення.
lubos hasko

18
Я б точно додав, де T: IConvertible
MikeT

5
Тип T повинен бути неконвертованим, а Тип бази. Варіант повинен.
chapluck

74

Метод Любоса Хаско не справляється з нульовими значеннями. Наведений нижче метод буде працювати для нульових значень. Я цього не придумав. Я знайшов це через Google: http://web.archive.org/web/20101214042641/http://dogaoztuzun.com/post/C-Generic-Type-Conversion.aspx Кредит до "Tuna Toksoz"

Спочатку використання:

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

я додав би варіанти конвертування Fallback для Enums та інших складних структур, але гарний дзвінок.
Томер Ш

2
Чому RegisterTypeConverter? Чи потрібно реєструвати перетворювачі перед рукою? (на жаль, посилання мертва, тому я не зміг її прочитати)
Cohen

Для декількох конверсій ви, мабуть, повинні створити tc( TypeConverter) лише один раз. TypeConverterє повільним, оскільки для пошуку цього використовується рефлексія TypeConverterAttribute. Якщо ви ініціалізуєте одне приватне TypeConverterполе, то ви повинні мати можливість повторного використання TypeConverterбагато разів.
Кевін П. Райс

Добре працює, але якщо T є object, викидає виняток. Я зміг це вирішити, використовуючи `if (typeof (T) .IsPrimitive) {return TConverter.ChangeType <T> (StringValue); } else {object o = (object) StringValue; повернутися до; } `як заміна зразка використання TConverter.ChangeType<T>(StringValue)
Метт

14

Для багатьох типів (цілочисельні, подвійні, DateTime тощо) існує метод статичного аналізу. Ви можете викликати його за допомогою відображення:

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

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

8
TypeDescriptor.GetConverter(PropertyObject).ConvertFrom(Value)

TypeDescriptorце клас, що має метод, GetConvertorякий приймає Typeоб'єкт, і тоді ви можете викликати ConvertFromметод для перетворення valueцього вказаного об'єкта.


Я особисто думаю, що цей інтерфейс краще для обробки конверсій замість методу Convert.ChangeType, оскільки вам потрібно реалізувати інтерфейс IConvertible у всіх своїх класах.
Sauleil

3

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

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

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


Загальних версій у C # не існує.
Шиммі Вайцхандлер

3

Перевірте статичність Nullable.GetUnderlyingType . - Якщо базовий тип є нульовим, то параметр шаблону не є Nullable, і ми можемо використовувати цей тип безпосередньо. Якщо базовий тип не є нульовим, то використовуйте базовий тип у перетворенні.

Здається, для мене це працює:

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");
}

1

Маючи натхнення з відповіді Боба , ці розширення також підтримують перетворення нульових значень і все примітивне перетворення назад і по-четверте.

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>();

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

Я використовую перетворення через об'єкт. Це трохи простіше.


дякую, я повинен перетворити T в інтерфейс, і простий конвертаційний об'єкт T працює правильно, легко і швидко дякую
LXG

5
(int) (об’єкт) "54"; це ФАТАЛЬНІСТЬ !, це не VB!
Tomer W

0

Я використав відповідь лобоса, і це працює. Але у мене виникли проблеми з конвертацією пар, через налаштування культури. Тому я додав

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

0

Ще одна варіація. Обробляє Nullables, а також ситуацій, коли рядок є нульовим, а T не є нульовим .

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(); }
    }
}

0

Ви можете зробити це в одному рядку, як показано нижче:

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

Щасливе кодування;)


Коли ви поділилися кодом як відповідь, будь ласка, спробуйте пояснити його.
Юнус Темурленк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.