Приведение объекта без какой-либо дополнительной информации, чем его System.Type - PullRequest
3 голосов
/ 08 февраля 2011

Для вопроса SO я совсем недавно написал общий метод расширения, который должен загружать объект из другого, то есть назначать все свойства источника для цели и делать это рекурсивно, если свойство является ссылкой -тип. Я довольно далеко использовал рефлексию, но столкнулся с проблемой, когда дело дошло до типов свойств, которые являются ссылочными, вот мой первый подход:

Первый подход:

    public static void Load<T>(this T target, T source, bool deep)
    {
        foreach (PropertyInfo property in typeof(T).GetProperties())
        {
            if (property.CanWrite && property.CanRead)
            {
                if (!deep || property.PropertyType.IsPrimitive || property.PropertyType == typeof(String))
                {
                    property.SetValue(target, property.GetValue(source, null), null);
                }
                else
                {
                    property.GetValue(target, null).Load(property.GetValue(source, null), deep);
                }
            }
        }
    }

Проблема здесь в том, что PropertyInfo.GetValue возвращает объект, впоследствии T будет равняться object в рекурсивном вызове, и я больше не могу получить свойства, которые на самом деле имеет объект.

Я придумал обходной путь, который требует от вас явной передачи Типа, что довольно избыточно, поскольку теоретически можно обойтись без:

    public static void Load<T>(this T target, Type type, T source, bool deep)
    {
        foreach (PropertyInfo property in type.GetProperties())
        {
            if (property.CanWrite && property.CanRead)
            {
                if (!deep || property.PropertyType.IsPrimitive || property.PropertyType == typeof(String))
                {
                    property.SetValue(target, property.GetValue(source, null), null);
                }
                else
                {
                    object targetPropertyReference = property.GetValue(target, null);
                    targetPropertyReference.Load(targetPropertyReference.GetType(), property.GetValue(source, null), deep);
                }
            }
        }
    }

Я также пытался использовать dynamic targetPropertyReference, но затем я получаю исключение времени выполнения, что метод Load не может быть найден, это бесит.

Кроме этого Convert.ChangeType также легко возвращает кровавый object, и я не могу иначе привести объект к тому, что он есть. Конечно, я искал ответ на этот вопрос в сети, но мне пока не удалось.

1 Ответ

2 голосов
/ 08 февраля 2011

Я не смотрел полный код и не рассматривал все последствия реализации такой схемы (РЕДАКТИРОВАТЬ: что произойдет, если есть циклические ссылки?), Но я могу дать вам два коротких исправления для вашего конкретного проблема:

Вариант 1. Уклонение от проблемы

Используйте тип времени выполнения предоставленного «обычного» аргумента, а не тип -argument .

Заменить:

typeof(T).GetProperties()

на:

// You need null-checks around this:
// you can't realistically continue if target is null anyway.
target.GetType().GetProperties()

Это, конечно, вносит незначительное семантическое изменение в ваш код, так как оно предотвращаетСценарии, когда кто-то хотел бы передать Giraffe, но хотел бы скопировать только свойства Animal.Он также взорвется, если source и target имеют разные типы времени выполнения (с разными свойствами), но вы можете обойти это без особых проблем (например, найдите самый глубокий общий базовый тип и используйте вместо этого свойства).


Вариант 2: Больше отражения / динамический

Другое решение, конечно, заключается в использовании MakeGenericMethod, чтобы разрешитьаргумент типа должен быть предоставлен «динамически».Это сохраняет первоначальную семантику кода (не проверено):

typeof(MyClass).GetMethod("Load")
               .MakeGenericMethod(property.PropertyType)
               .Invoke(null, property.GetValue(target, null), 
                             property.GetValue(source, null), deep);

Кстати, есть нет причина, по которой вы не можете использовать dynamic для выполнения чего-то очень похожего.Я подозреваю, что вы пытаетесь сделать вызов "как" метод расширения, , но это не сработает .Просто попробуйте обычный синтаксис "static-method".

...