Подтвердить что ты не робот

Синтаксический анализ строки С# на любой объект

Я храню значения объектов в строках, например,

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");
4b9b3361

Ответ 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).