C # как определить универсальный тип динамически

2

У меня есть метод, в котором мне нравится выполнять несколько разных приведений, используя механизм отражения.

private static T GetObject<T>(Dictionary<string, object> dict)
{
    Type type = typeof(T);
    var obj = Activator.CreateInstance(type);
    foreach (var kv in dict)
    {
        var p = type.GetProperty(kv.Key);
        Type t = p.PropertyType;
        Type t2 = kv.Value.GetType();
        if (t == t2)
        {
            p.SetValue(obj, kv.Value);
        }
        else if (!IsPrimitive(p.PropertyType)) 
        {
            p.SetValue(obj, GetObject<class of p>((Dictionary<string, object>) kv.Value)); //???
        }
        else
        {
            p.SetValue(obj, (primitive)(kv.Value)); //???                    
        }
    }
    return (T)obj;
}

РЕДАКТИРОВАНИЕ

У меня есть словарь, и я хочу преобразовать его в пользовательский класс, каждый ключ из словаря является свойством, а каждое значение словаря является значением этого свойства. Проблема возникает в двух случаях, когда оба, тип свойства класса и тип значения словаря являются примитивами, но имеют различный тип (например, свойство имеет тип int, но значение словаря является длинным), вторая проблема возникает, когда значением свойства является другой пользовательский класс и в в этом случае значение словаря всегда другой словарь

Как я могу динамически определять необходимые броски/приведения?

  • 0
    Я не уверен, что полностью понимаю, что вы пытаетесь сделать. Просто получить все объекты типа T из словаря, исключая при этом все, что не относится к типу T? Что если есть несколько объектов T, вы должны вернуть их все или только первый?
  • 0
    Вашему T нужен конструктор по умолчанию, почему бы не ограничить его тем, where T: new и просто var obj = new T(); , Что ты пытаешься сделать? Это просто построить словарь, который представляет свойства (имя и значение) объекта. Для этого вам не нужно ничего приводить (вы храните его в Dictionary<string,object> , поэтому все, что вам нужно - это оставить их как объект).
Показать ещё 4 комментария
Теги:
reflection

2 ответа

2

Начните с поиска, совместимы ли типы с

if (t.IsAssignableFrom(t2))

В противном случае конвертировать с

object converted = Convert.ChangeType(kv.Value, t);

Это должно обрабатывать много случаев.

Поскольку вы должны вызывать метод рекурсивно с типом, известным только во время выполнения, лучше иметь не универсальную перегруженную версию GetObject. Оригинальный метод вызывает только неуниверсальный

private static T GetObject<T>(Dictionary<string, object> dict)
    where T : class, new() // Suggested by Brett Caswell.
{
    return (T)GetObject(dict, typeof(T));
}

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

private static object GetObject(Dictionary<string, object> dict, Type objectType)
{
    object obj = Activator.CreateInstance(objectType);
    foreach (var kv in dict) {
        PropertyInfo prop = objectType.GetProperty(kv.Key);
        Type propType = prop.PropertyType;
        object value = kv.Value;
        if (value == null) {
            if (propType.IsValueType) { // Get default value of type.
                value = Activator.CreateInstance(propType);
            }
        } else if (value is Dictionary<string, object> nestedDict) {
            value = GetObject(nestedDict, propType);
        } else if (!propType.IsAssignableFrom(value.GetType())) {
            value = Convert.ChangeType(value, propType);
        }
        prop.SetValue(obj, value);
    }
    return obj;
}
  • 0
    Я думаю, что ваше решение только для примитивных типов. в противном случае я должен вызвать рекурсивный метод, но я не знаю, как написать приведение
  • 1
    Type.IsAssignableFrom хорошо обрабатывает иерархии классов, это определенно не только для примитивных типов.
Показать ещё 4 комментария
-1

Лучшим подходом к решению этой проблемы является применение одного из принципов SOLID дяди Боба - принципа Open close. Вот классический пример этого принципа

public abstract class Shape
{
        public abstract double Area();
 } 

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    public override double Area()
    {
        return Width*Height;
    }
}

public class Circle : Shape
{
    public double Radius { get; set; }
    public override double Area()
    {
        return Radius*Radius*Math.PI;
    }
} 

таким образом, вам не нужно создавать дерево if else, но с реализацией абстрактного класса вы должны реализовывать метод Area каждый раз, когда вы используете его с любым вашим классом. Надеюсь, что служат цели

  • 0
    Я не уверен, что это имеет отношение. Я думаю, что он использует рефлексию здесь при условии, что он не знает типы до времени выполнения.
  • 0
    @BrettCaswell у тебя есть право. Простой вопрос: могу ли я передавать универсальные типы в метод во время выполнения?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню