Как отражение может не привести к запахам кода? - PullRequest
63 голосов
/ 04 февраля 2010

Я из языков низкого уровня - C ++ - это самый высокий уровень, на котором я программирую.

Недавно я наткнулся на Reflection, и я просто не могу понять, как его можно использовать без запахов кода.

Идея проверки класса / метода / функции во время выполнения, на мой взгляд, указывает на недостаток в дизайне - я думаю, что большинство проблем, которые Reflection (пытается) решить, может быть использовано либо с полиморфизмом, либо с правильным использованием наследования. 1005 *

Я не прав? Я неправильно понимаю концепцию и полезность Reflection?

Я ищу хорошее объяснение того, когда использовать Reflection, когда другие решения не удастся или будут слишком громоздкими для реализации, а также когда НЕ использовать.

Пожалуйста, просветите этот низкоуровневый смазочный материал.

Ответы [ 18 ]

2 голосов
/ 04 февраля 2010

Идея заключалась в том, чтобы иметь возможность запрашивать любые свойства объектов GUI, предоставлять их в графическом интерфейсе для настройки и предварительной настройки. Теперь его использование было расширено и доказало свою целесообразность.

РЕДАКТИРОВАТЬ: орфография

2 голосов
/ 04 февраля 2010

Плагины являются отличным примером.

Инструменты - еще один пример - инструменты инспектора, инструменты сборки и т. Д.

2 голосов
/ 04 февраля 2010

Все дело в быстром развитии.

var myObject = // Something with quite a few properties.
var props = new Dictionary<string, object>();
foreach (var prop in myObject.GetType().GetProperties())
{
    props.Add(prop.Name, prop.GetValue(myObject, null);
}
1 голос
/ 15 мая 2012

Одно использование еще не упомянуто: хотя отражение обычно считается «медленным», можно использовать Reflection для повышения эффективности кода, который использует интерфейсы, такие как IEquatable<T>, когда они существуют, и использует другие средства проверки равенства, когда они не. В отсутствие отражения код, который хотел бы проверить, равны ли два объекта, должен будет либо использовать Object.Equals(Object), либо проверить во время выполнения , реализован ли объект IEquatable<T>, и, если это так, привести объект к этому интерфейсу. В любом случае, если тип сравниваемой вещи является типом значения, потребуется как минимум одна операция упаковки. Использование Reflection позволяет классу EqualityComparer<T> автоматически создавать реализацию для конкретного типа IEqualityComparer<T> для любого конкретного типа T, причем эта реализация использует IEquatable<T>, если он определен, или Object.Equals(Object), если он не является. При первом использовании EqualityComparer<T>.Default для любого конкретного типа T система должна будет выполнить больше работы, чем требовалось бы, чтобы один раз проверить, реализует ли конкретный тип IEquatable<T>. С другой стороны, как только эта работа будет выполнена, больше не потребуется проверка типов во время выполнения, поскольку система создаст пользовательскую реализацию EqualityComparer<T> для рассматриваемого типа.

1 голос
/ 04 февраля 2010

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

Или более простой пример в домене, где вы хотите преобразовать данные из базы данных в объекты данных без необходимости изменять код преобразования при изменении свойств, если поддерживаются соглашения (в этом случае сопоставление имен свойств и определенный атрибут):

    ///--------------------------------------------------------------------------------
    /// <summary>Transform data from the input data reader into the output object.  Each
    /// element to be transformed must have the DataElement attribute associated with
    /// it.</summary>
    ///
    /// <param name="inputReader">The database reader with the input data.</param>
    /// <param name="outputObject">The output object to be populated with the input data.</param>
    /// <param name="filterElements">Data elements to filter out of the transformation.</param>
    ///--------------------------------------------------------------------------------
    public static void TransformDataFromDbReader(DbDataReader inputReader, IDataObject outputObject, NameObjectCollection filterElements)
    {
        try
        {
            // add all public properties with the DataElement attribute to the output object
            foreach (PropertyInfo loopInfo in outputObject.GetType().GetProperties())
            {
                foreach (object loopAttribute in loopInfo.GetCustomAttributes(true))
                {
                    if (loopAttribute is DataElementAttribute)
                    {
                        // get name of property to transform
                        string transformName = DataHelper.GetString(((DataElementAttribute)loopAttribute).ElementName).Trim().ToLower();
                        if (transformName == String.Empty)
                        {
                            transformName = loopInfo.Name.Trim().ToLower();
                        }

                        // do transform if not in filter field list
                        if (filterElements == null || DataHelper.GetString(filterElements[transformName]) == String.Empty)
                        {
                            for (int i = 0; i < inputReader.FieldCount; i++)
                            {
                                if (inputReader.GetName(i).Trim().ToLower() == transformName)
                                {
                                    // set value, based on system type
                                    loopInfo.SetValue(outputObject, DataHelper.GetValueFromSystemType(inputReader[i], loopInfo.PropertyType.UnderlyingSystemType.FullName, false), null);
                                }
                            }
                        }
                    }
                }
            }

            // add all fields with the DataElement attribute to the output object
            foreach (FieldInfo loopInfo in outputObject.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance))
            {
                foreach (object loopAttribute in loopInfo.GetCustomAttributes(true))
                {
                    if (loopAttribute is DataElementAttribute)
                    {
                        // get name of field to transform
                        string transformName = DataHelper.GetString(((DataElementAttribute)loopAttribute).ElementName).Trim().ToLower();
                        if (transformName == String.Empty)
                        {
                            transformName = loopInfo.Name.Trim().ToLower();
                        }

                        // do transform if not in filter field list
                        if (filterElements == null || DataHelper.GetString(filterElements[transformName]) == String.Empty)
                        {
                            for (int i = 0; i < inputReader.FieldCount; i++)
                            {
                                if (inputReader.GetName(i).Trim().ToLower() == transformName)
                                {
                                    // set value, based on system type
                                    loopInfo.SetValue(outputObject, DataHelper.GetValueFromSystemType(inputReader[i], loopInfo.FieldType.UnderlyingSystemType.FullName, false));
                                }
                            }
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            bool reThrow = ExceptionHandler.HandleException(ex);
            if (reThrow) throw;
        }
    }
1 голос
/ 04 февраля 2010

Очень простой пример на Python. Предположим, у вас есть класс, который имеет 3 метода:

class SomeClass(object):
    def methodA(self):
       # some code
    def methodB(self):
       # some code
    def methodC(self):
       # some code

Теперь в каком-то другом классе вы хотите украсить эти методы некоторым дополнительным поведением (то есть вы хотите, чтобы этот класс имитировал SomeClass, но с дополнительной функциональностью). Это так просто, как:

class SomeOtherClass(object):
    def __getattr__(self, attr_name):
        # do something nice and then call method that caller requested
        getattr(self.someclass_instance, attr_name)()
1 голос
/ 04 февраля 2010

Без размышлений плагин не будет работать!

1 голос
/ 04 февраля 2010

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

Кроме того, если вы работаете с редактором, который на самом деле не заботится о базовой модели, а скорее о том, как объекты структурируются напрямую, ala System.Forms.PropertyGrid)

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