C # общая строка разбора на любой объект - PullRequest
27 голосов
/ 19 октября 2010

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

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

. Причина, по которой мне нужны такие функции, заключается в том, что я храню указанные значения в базе данных и хочу прочитать их с помощью запроса, а затем инициализировать соответствующие свойстваобъект.Это будет возможно?Весь объект не хранится в базе данных, только несколько полей, которые я хотел бы прочитать и установить динамически.Я знаю, что могу делать это статически, однако это станет утомительным, трудным в обслуживании и склонным к ошибкам с многочисленными различными полями / свойствами, которые читаются.

РЕДАКТИРОВАТЬ: Бонусные баллы, если AwesomeFunction может работать с пользовательскимиклассы, которые определяют конструктор, который принимает строку!

EDIT2: Тип назначения можно узнать через PropertyType, в конкретном случае, когда я хочу использовать этот тип функциональности.Я думаю, что Enums будет легко разобраться с этим, например,

Type destinationType = info.PropertyType;
Enum.Parse(destinationType, "MyValue");

Ответы [ 4 ]

37 голосов
/ 19 октября 2010

Возможно, первое, что нужно попробовать:

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);
}
4 голосов
/ 29 мая 2015
public T Get<T>(string val)
{
    if (!string.IsNullOrWhiteSpace(val))
        return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromString(val);
    else 
        return default(T);
}
3 голосов
/ 19 октября 2010

Вот простая версия:

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

Он распознает целые и двойные числа, но все остальное возвращается в виде строки.Проблема с обработкой перечислений заключается в том, что нет способа узнать, к какому перечислению относится значение, и нет способа определить, должно ли оно быть строкой или нет.Другие проблемы заключаются в том, что он не обрабатывает даты / время или десятичные дроби (как бы вы отличали их от двойных?) И т. Д.

Если вы готовы изменить свой код следующим образом:

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

Редактировать: Добавлена ​​пропущенная скобка

0 голосов
/ 29 октября 2011

Я знаю, что это не отвечает на ваш вопрос, но вы смотрели на Dapper micro ORM ?
Это очень просто (по сравнению с 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 упакован в один файл и, как сообщается, используется командой StackOverflow (кроме Linq to SQL).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...