Синтаксический анализ строки С# на любой объект
Я храню значения объектов в строках, например,
string[] values = new string[] { "213.4", "10", "hello", "MyValue"};
Есть ли какой-либо способ для общего инициализации соответствующих типов объектов? например, что-то вроде
double foo1 = AwesomeFunction(values[0]);
int foo2 = AwesomeFunction(values[1]);
string foo3 = AwesomeFunction(values[2]);
MyEnum foo4 = AwesomeFunction(values[3]);
где AwesomeFunction
- это функция, которая мне нужна. Конечным применением является инициализация свойств, например,
MyObject obj = new MyObject();
PropertyInfo info = typeof(MyObject).GetProperty("SomeProperty");
info.SetValue(obj, AwesomeFunction("20.53"), null);
Причина, по которой мне нужна такая функциональность, заключается в том, что я храню указанные значения в базе данных и хочу прочитать их через запрос и затем инициализировать соответствующие свойства объекта. Это будет возможно? Весь объект не хранится в базе данных, всего несколько полей, которые я бы хотел прочитать и установить динамически. Я знаю, что могу сделать это статически, однако это будет утомительно, трудно поддерживать, и читаются ошибки с множеством различных полей/свойств.
EDIT: бонусные точки, если AwesomeFunction
может работать с пользовательскими классами, которые определяют конструктор, который принимает строку!
EDIT2: Тип адресата можно узнать через PropertyType, в конкретном случае, когда я хочу использовать этот тип функций. Я думаю, что Enums было бы легко разобрать с этим, например,
Type destinationType = info.PropertyType;
Enum.Parse(destinationType, "MyValue");
Ответы
Ответ 1
Возможно, первое, что нужно попробовать:
object value = Convert.ChangeType(text, info.PropertyType);
Однако это не поддерживает расширяемость через пользовательские типы; , если вам нужно это, как насчет:
TypeConverter tc = TypeDescriptor.GetConverter(info.PropertyType);
object value = tc.ConvertFromString(null, CultureInfo.InvariantCulture, text);
info.SetValue(obj, value, null);
Или:
info.SetValue(obj, AwesomeFunction("20.53", info.PropertyType), null);
с
public object AwesomeFunction(string text, Type type) {
TypeConverter tc = TypeDescriptor.GetConverter(type);
return tc.ConvertFromString(null, CultureInfo.InvariantCulture, text);
}
Ответ 2
Вот простая версия:
object ConvertToAny(string input)
{
int i;
if (int.TryParse(input, out i))
return i;
double d;
if (double.TryParse(input, out d))
return d;
return input;
}
Он распознает ints и double, но все остальное возвращается как строка. Проблема с обработкой перечислений заключается в том, что нет способа узнать, что перечисляет значение, и нет способа определить, должна ли она быть строкой или нет. Другие проблемы заключаются в том, что они не обрабатывают даты/время или десятичные числа (как бы вы отличали их от парных?) И т.д.
Если вы хотите изменить свой код следующим образом:
PropertyInfo info = typeof(MyObject).GetProperty("SomeProperty");
info.SetValue(obj, AwesomeFunction("20.53", info.PropertyType), null);
Затем это становится значительно проще:
object ConvertToAny(string input, Type target)
{
// handle common types
if (target == typeof(int))
return int.Parse(input);
if (target == typeof(double))
return double.Parse(input);
...
// handle enums
if (target.BaseType == typeof(Enum))
return Enum.Parse(target, input);
// handle anything with a static Parse(string) function
var parse = target.GetMethod("Parse",
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.Public,
null, new[] { typeof(string) }, null);
if (parse != null)
return parse.Invoke(null, new object[] { input });
// handle types with constructors that take a string
var constructor = target.GetConstructor(new[] { typeof(string) });
if (constructor != null)
return constructor.Invoke(new object[] { input });
}
Изменить: добавлена отсутствующая скобка
Ответ 3
public T Get<T>(string val)
{
if (!string.IsNullOrWhiteSpace(val))
return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromString(val);
else
return default(T);
}
Ответ 4
Я знаю, что это не отвечает на ваш вопрос, но вы посмотрели на Dapper micro ORM?
Это über-simple (по сравнению с LINQ to SQL или, по этой причине, Entity Framework) и делает то, что вы хотите.
Рассмотрим это:
public class Dog
{
public int? Age { get; set; }
public Guid Id { get; set; }
public string Name { get; set; }
public float? Weight { get; set; }
}
var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select * from Dogs where Id = @Id",
new { Id = 42 }
).First();
Сам Dapper упакован в один файл и, как сообщается, fooobar.com/info/tagged/... (кроме Linq to SQL).