У меня есть класс, который я хочу использовать для хранения «свойств» для другого класса. Эти свойства просто имеют имя и значение. В идеале я хотел бы иметь возможность добавлять типизированные свойства, чтобы возвращаемое «значение» всегда имело тот тип, который я хочу.
Тип всегда должен быть примитивным. Этот класс подклассов абстрактный класс, который в основном хранит имя и значение в виде строки. Идея в том, что этот подкласс добавит некоторую безопасность типов к базовому классу (а также сэкономит мне на некотором преобразовании).
Итак, я создал класс, который (примерно) это:
public class TypedProperty<DataType> : Property
{
public DataType TypedValue
{
get { // Having problems here! }
set { base.Value = value.ToString();}
}
}
Итак, вопрос:
Есть ли «общий» способ преобразования строки обратно в примитив?
Я не могу найти какой-либо универсальный интерфейс, который связывает преобразование по всем направлениям (что-то вроде ITryParsable было бы идеально!).
Я не уверен, правильно ли я понял ваши намерения, но давайте посмотрим, поможет ли это.
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();}
}
}
Convert.ChangeType
это не очень универсальное и расширяемое решение, оно работает для большинства основных типов. если нужно что-то лучшее, нет проблем, чтобы обернуть этот метод во что-то большее, как предложил Тим, или использовать другой метод преобразования. Вы можете использовать такую конструкцию, как класс черт . Таким образом, у вас будет параметризованный вспомогательный класс, который знает, как преобразовать строку в значение ее собственного типа. Тогда ваш геттер может выглядеть так:
get { return StringConverter<DataType>.FromString(base.Value); }
Теперь я должен отметить, что мой опыт работы с параметризованными типами ограничен C ++ и его шаблонами, но я предполагаю, что есть некоторый способ сделать то же самое с использованием обобщений C #.
Для многих типов (целое, двойное, DateTime и т. Д.) Существует статический метод Parse. Вы можете вызвать его, используя отражение:
MethodInfo m = typeof(T).GetMethod("Parse", new Type[] { typeof(string) } );
if (m != null)
{
return m.Invoke(null, new object[] { base.Value });
}
Метод Любоса Хаско не подходит для nullables. Метод ниже будет работать для обнуляемых. Я не придумал это, все же. Я нашел его через 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)));
}
}
tc
( TypeConverter
) только один раз. TypeConverter
медленный, потому что он использует отражение для поиска TypeConverterAttribute
. Если вы инициализируете одно приватное TypeConverter
поле, вы сможете использовать его TypeConverter
много раз. object
, выдает исключение. Я смог обойти это, используя `if (typeof (T) .IsPrimitive) {return TConverter.ChangeType <T> (StringValue); } else {object o = (object) StringValue; Вернуться к; } `в качестве замены для примера использования TConverter.ChangeType<T>(StringValue)
public class TypedProperty<T> : Property
{
public T TypedValue
{
get { return (T)(object)base.Value; }
set { base.Value = value.ToString();}
}
}
Я использую преобразование через объект. Это немного проще.
TypeDescriptor.GetConverter(PropertyObject).ConvertFrom(Value)
TypeDescriptor
это класс, имеющий метод, GetConvertor
который принимает Type
объект, а затем вы можете вызвать ConvertFrom
метод для преобразования value
указанного объекта.
Я использовал ответ Лобоса, и он работает. Но у меня была проблема с конвертацией двойников из-за настроек культуры. Итак, я добавил
return (T)Convert.ChangeType(base.Value, typeof(T), CultureInfo.InvariantCulture);
Проверьте статический 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");
}
Еще один вариант. Обрабатывает 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(); }
}
}
Вдохновленные ответом Боба , эти расширения также поддерживают преобразование нулевого значения и все примитивное преобразование назад и четвертое.
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>();
Вы можете сделать это в одну строку, как показано ниже:
YourClass obj = (YourClass)Convert.ChangeType(YourValue, typeof(YourClass));
Удачного кодирования;)