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

С#: Может кто-нибудь объяснить практичность отражения?

Итак, я попытался найти SO, надеясь, что у кого-то есть хорошее объяснение этого, без везения.

Я спросил другого друга, другого вопроса (который я сейчас забыл), и его ответ был просто "отражением", прежде чем он подписал контракт.

Я все еще очень новичок в мире С#, будучи любительским программистом VB.net(также JavaScript, ActionScript и C), и стараюсь изо всех сил стараться понять эти передовые концепции.

Там много философских ответов - "приложение, смотрящее на себя", - но они не дают никаких практических советов о том, что на самом деле происходит или как оно используется в этом контексте.

Итак, что такое отражение, почему это важно, и почему/как его использовать?

4b9b3361

Ответ 1

Отражение предоставляет возможность определять вещи и выполнять код во время выполнения.

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

Например:

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

b) Используя отражение, вы можете генерировать классы и код "на лету", что упрощает определенные задачи программирования, поскольку программисту не нужно явно создавать все необходимые коды.

c) Отражение также неоценимо для программ, которые работают, исследуя код. Примером этого может быть IDE или дизайнер пользовательского интерфейса.

d) Отражение помогает вам уменьшить код шаблона.

e) Отражение полезно для определения мини-доменных языков (DSL) в вашем коде.

Ответ 2

(моя дефиниция) Отражение - это возможность писать статический код, который выполняет код во время выполнения, что обычно определяется во время компиляции.

Например, я мог бы вызвать метод класса для рисования путем компиляции в этой команде, например:

pen.DrawLine()

или С отражением я могу сначала увидеть, имеет ли мой объект метод под названием "drawline", и если да, вызовите его. (Обратите внимание, что это не фактический синтаксис С# Reflection)

 if(pen.Methods.Contains("DrawLine"))
 {
    pen.InvokeMethod("DrawLine"))
 }

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

С отражением я могу загрузить сборку .NET(DLL в этом случае) во время выполнения, узнать все типы, которые есть в сборке .NET, посмотреть, реализует ли какой-либо из этих типов определенный интерфейс, и если это так, создайте экземпляр класса, к которому я вызываю методы интерфейса.

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

Ответ 3

Наиболее частое использование рефлексии - это расширение того, что раньше называлось RTTI (информация о типе времени выполнения) и в основном являлось доменом программистов на С++.

Отражение является побочным эффектом способа построения .net, и Microsoft решила открыть библиотеки, которые они использовали для создания Visual Studio и времени выполнения .net для разработчиков за пределами Microsoft.

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

    public static IMyCompanySetting UnwrapSetting(XmlNode settingNode)
    {

        string typeName = settingNode.Attributes["type"].Value;
        string typeAssembly;
        if(settingNode.Attributes["assembly"] != null)
        {
            typeAssembly = settingNode.Attributes["assembly"].Value;
        }

        Type settingType = null;
        Assembly settingAssembly = null;
        try
        {
            // Create an object based on the type and assembly properties stored in the XML
            try
            {
                settingAssembly = Assembly.Load(typeAssembly);
                if (settingAssembly == null)
                {
                    return null;
                }
            }
            catch (Exception outerEx)
            {
                try
                {
                    settingType = GetOrphanType(typeName);
                }
                catch (Exception innerEx)
                {
                    throw new Exception("Failed to create object " + typeName + " :: " + innerEx.ToString(), outerEx);
                }
            }

            // We will try in order:
            // 1. Get the type from the named assembly.
            // 2. Get the type using its fully-qualified name.
            // 3. Do a deep search for the most basic name of the class.
            if (settingType == null && settingAssembly != null) settingType = settingAssembly.GetType(typeName);
            if (settingType == null) settingType = Type.GetType(typeName);
            if (settingType == null) settingType = GetOrphanType(typeName);
            if (settingType == null) throw new System.Exception(
                String.Format("Unable to load definition for type {0} using loosest possible binding.", typeName));
        }
        catch (Exception ex)
        {
            throw new CometConfigurationException(
                String.Format("Could not create object of type {0} from assembly {1}", typeName, typeAssembly), ex);
        }

        bool settingIsCreated = false;
        IMyCompanySetting theSetting = null;

        // If the class has a constructor that accepts a single parameter that is an XML node,
        // call that constructor.
        foreach (ConstructorInfo ctor in settingType.GetConstructors())
        {
            ParameterInfo[] parameters = ctor.GetParameters();
            if (parameters.Length == 1)
            {
                if (parameters[0].ParameterType == typeof(XmlNode))
                {
                    object[] theParams = { settingNode };
                    try
                    {
                        theSetting = (IMyCompanySetting)ctor.Invoke(theParams);
                        settingIsCreated = true;
                    }
                    catch (System.Exception ex)
                    {
                        // If there is a pre-existing constructor that accepts an XML node
                        // with a different schema from the one provided here, it will fail
                        // and we'll go to the default constructor.
                        UtilitiesAndConstants.ReportExceptionToCommonLog(ex);
                        settingIsCreated = false;
                    }
                }
            }
        }

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

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

Загрузите сборку во время выполнения на основе ее имени.

Загрузите объект из сборки во время выполнения на основе его имени.

Вызов конструктора объекта на основе подписи для объекта класса, который неизвестен во время компиляции.

Ответ 4

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

С отражением вы можете просто прочитать имя класса, реализация которого вы хотите использовать из файла конфигурации (в виде строки), и создать экземпляр экземпляра этого класса.

Ответ 5

Отражение позволяет вам встраивать в сборку и использовать ее, независимо от того, на которой она не указана позже.

Рассмотрим систему плагинов, в которой хост не знает о плагинах, которые он будет удерживать; с Reflection (и правильной архитектурой) вы можете построить простое решение, достигающее этого.

Рассмотрим другой сценарий, в котором вы должны вызывать метод для объекта с заданной строкой, Reflection дает вам также подход для достижения этого.

Есть много других применений, но я надеюсь, что эти два, откройте свой аппетит к этой отличной функции CLR

Ответ 6

Иногда полезно иметь возможность читать свойства или вызывать методы класса, не зная, какие свойства или методы имеет класс во время разработки. Путь к этому - размышление. Как показано в приведенном ниже коде, вы можете получить список всех свойств в классе и получить их значения, не зная об этом во время компиляции. Или вы можете получить метод по имени, даже если вы не знаете имя метода во время компиляции и вызываете его через отражение. Это позволит вам, например, создать язык сценариев, который работает с объектами, определенными в другой пользовательской DLL. (Конечно, вы также можете перечислить все методы в классе или получить определенное свойство по имени, но эти случаи не показаны в приведенном ниже коде.)

class Program
{
  static void Main(string[] args)
  {
     UserDefinedClass udc = new UserDefinedClass();
     udc.UserProperty = "This is the property value";

     ClassTracer ct = new ClassTracer(udc);
     ct.TraceProperties();
     ct.CallMethod("UserFunction", "parameter 1 value");
  }
}

class ClassTracer
{
  object target;

  public ClassTracer(object target)
  {
     this.target = target;
  }

  public void TraceProperties()
  {
     // Get a list of all properties implemented by the class
     System.Reflection.PropertyInfo[] pis = target.GetType().GetProperties();
     foreach (System.Reflection.PropertyInfo pi in pis)
     {
        Console.WriteLine("{0}: {1}", pi.Name, pi.GetValue(target, null));
     }
  }

  public void CallMethod(string MethodName, string arg1)
  {
     System.Reflection.MethodInfo mi = target.GetType().GetMethod(MethodName);
     if (mi != null)
     {
        mi.Invoke(target, new object[] { arg1 });
     }
  }
}

class UserDefinedClass
{
  private string userPropertyValue;

  public string UserProperty
  {
     get
     {
        return userPropertyValue;
     }
     set
     {
        userPropertyValue = value;
     }
  }

  public void UserFunction(string parameter)
  {
     Console.WriteLine("{0} - {1}", userPropertyValue, parameter);
  }
}

Ответ 7

Отражение использует код для изучения самого кода. Например, вместо вызова foo.DoBar() вы можете вызвать:

foo.GetType().GetMethod("DoBar").Invoke(foo,null);

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

Дополнительные сведения см. в разделе MSDN раздела Reflection.

Ответ 8

Существует много случаев, когда приложение не должно много думать о себе и должно посмотреть на время выполнения, чтобы узнать, что это на самом деле. Именно здесь в шоу приходит отражение. Например, ASP.NET не делает предположений о используемом провайдере членства, кроме того, что он наследует соответствующий класс. Он использует отражение для поиска класса во время выполнения и его экземпляра. Это дает большую расширяемость из-за развязки и уменьшения зависимостей между классами.

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

Ответ 9

Эндрю Троелсен, автор Pro С# 2008 и .NET 3.5 Platform, определяет отражение таким образом:

В юниверсе .NET отражение - это процесс обнаружения типа времени выполнения.

Я не уверен, что могу дать вам точное объяснение того, что это значит, но я могу рассказать вам, как я его использовал.

Я могу поместить пользовательский атрибут в сборку (dll). Используя отражение во время выполнения, я могу проверить все сборки в определенном каталоге, чтобы определить, есть ли у них этот атрибут. Это может привести мое приложение в том, как использовать сборку, например плагин.

Я также использовал отражение для загрузки и сохранения настроек приложения.


Для чего это стоит, если вы не используете отражение, я не уверен, что это связано с вашими исключениями в графическом интерфейсе.

О единственном, что я знаю о С# Отражение состоит в том, что это раздражает черт, потому что все мои приложения для графического интерфейса бросать невероятно раздражающие, бессмысленные "Исключение было вызвано цель вызова" в Application.Run(new frmMain()); вместо того, чтобы прекратить проблема (как показано в InnerException).

Ваше выражение заставляет меня думать, что у вас мало, если есть какие-либо блоки try/catch в любом месте вашего приложения, так что каждое исключение переносится обратно в верхнюю часть стека вызовов. Если вы используете Visual Studio 2005/2008 в режиме отладки, зайдите в меню "Отладка" и выберите пункт "Исключения...". В диалоговом окне "Исключения" установите флажки в столбце "Бросок". Теперь, когда вы запускаете приложение и генерируется исключение, отладчик будет разбит, где вызывается исключение.

Ответ 10

Скажем, у вас есть некоторые бизнес-сущности, все из которых происходят из базового класса Entity. Позвольте также сказать, что вам нужно/хотите, чтобы все производные классы были клонируемыми. Вы можете реализовать метод "Clone" (интерфейс ICloneable) в базовом классе, который будет циклически выполнять все свойства текущего класса (хотя он реализован в классе Entity) и скопировать их в клонированный объект. Это случай, когда Reflection действительно может помочь. Потому что вы не можете знать имя и количество свойств в базовом классе. И вы не хотите внедрять метод во всех производных классах. Однако вы можете сделать метод виртуальным.

Ответ 11

Другие люди ответили за

что такое отражение, почему это важно

Поэтому я буду отвечать только на следующие вопросы.

Как его использовать?

Через следующие пространства имен

Почему я использую его?

Чтобы вызвать/проверить типы сборки, элементы, свойства

Когда вы смотрите на такой продукт, как Reflector, возможность проверки структуры кода более практична достаточно.

Ответ 12

Если вы бежите быстро и в ярости, возможно, вы наследуете от объекта и нуждаетесь в доступе к частному полю. У вас нет источника, но это нормально, у вас есть Reflection. (Хакки я знаю)

Более законное использование, которое я видел, - использовать рефлексию в реализации обобщенных файлов С#, где я хочу выполнить некоторую операцию на универсальном типе, который в противном случае недоступен. Единственными операциями, доступными для общего типа, являются те, которые доступны с общим ограничением (т.е. Если вы ограничиваете универсальный тип для реализации IFoo, вы можете использовать методы IFoo для экземпляров этого класса). Некоторые операции, хотя просто недоступны, например, я принимал общий класс, который должен был иметь конкретный конструктор, отличный от стандартного. Я не мог ограничить общий метод, чтобы принимать только общие параметры типа с этим конструктором, но по крайней мере я мог попробовать и использовать этот конструктор при реализации общего с помощью отражения.

Я использую рефлексию экономно, но иногда это пригодится.

Ответ 13

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

Например, с моим API привязок я могу написать следующее:

Связывание b = новое связывание (textBox, "Текст", myObject, "Текст", BindingMode.Bipirectional);

Любые изменения текста в объекте textBox обнаруживаются объектом Binding (который присоединяется к событию TextChanged) и передаются в свойство myObject.Text. Изменения в myObject.Text обнаруживаются (по событию TextChanged) и передаются в элемент textBox.Text.

Я реализовал эту систему, используя отражение. Конструктор Binding объявлен как:

Связывание (объект, строка, объект, строка, BindingMode)

Таким образом, система работает с любым объектом (с одним важным условием).

Я проверяю первый объект и нахожу член, соответствующий именованному члену в первой строке (textBox и "Text", т.е. имеет ли textBox член, называемый "Text" ). Повторите со вторым объектом и строкой.

Proviso: для объектов, которые будут использоваться в этой схеме, они должны выполнить неформальное требование о том, что любое свойство bindable должно иметь соответствующее событие PropertyNameChanged. К счастью, почти все компоненты .Net UI следуют этому соглашению.

Затем я проверяю объект на необходимые события PropertyNameChanged, добавляю к ним обработчики событий и все установлено.

NB. Я реализовал это в .Net 1.0, так что он предшествует внедрению Microsoft bindings, и я еще не обследовал его.

Ответ 14

Практическое применение

Используйте отражение для выравнивания данных с объектами, как в ситуации сопоставления данных. Например, вы можете сопоставить столбец базы данных с свойством объекта. Если вы хотите написать объект Relational Mapper, вам понадобится какой-то способ получить от параметра конфигурации (например, DatabaseColumnName maps to Class.MemberName) объектам, к которым он относится.

Ответ 15

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

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

Отражение позволяет избежать статического ввода во время компиляции, открывая типы, методы, поля и т.д. во время выполнения. Многие динамические языки на JVM (не уверены, что CLR, но id представляют себе то же самое верно) используют отражение для методов отправки.

Ответ 16

С точки зрения практического использования для размышлений мы использовали его, чтобы позволить нашим клиентам предоставлять свою собственную локализацию.

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

Ответ 17

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

Ответ 18

Можете ли вы опубликовать некоторый пример кода, который показывает, как вы выполняете отражение? У вас есть файлы PDB в том же месте, что и сборка, на которой вы используете API отражения? Если это так, то в Visual studio перейдите в меню Debug → Exceptions и установите флажок "Common Language runtime throw". Запустите приложение в режиме отладки. Надеюсь, отладчик должен сломаться в точке вашей отраженной сборки, на которой бросается реальное исключение.