.NET 4.5 CustomReflectionContext: для чего он нужен?

Что нового в.NET Framework 4.5 Developer Preview упоминает

Возможность настройки контекста отражения для переопределения поведения отражения по умолчанию через класс CustomReflectionContext.

Какова цель ReflectionContext? MSDN не совсем ясно по этому вопросу.

1 ответ

Решение

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

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

Классическим решением в.NET является добавление нескольких пользовательских атрибутов в соответствующие элементы. Однако одна из проблем заключается в том, что это может означать, что части вашего кода, которые выполняют значимую работу во время выполнения, в конечном итоге зависят от классов, которые делают что-либо только во время разработки - полагаясь на атрибуты, вы не можете отделить аспекты времени выполнения и времени разработки, Вы действительно хотите отправить код для пользовательского интерфейса дизайнера для панели свойств VS как часть библиотеки элементов управления, которая в конечном итоге окажется на компьютерах конечных пользователей?

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

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

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

Различные специальные решения появились для этого. У вас есть все TypeDescriptor а также PropertyDescriptor семейство типов, которые обеспечивают своего рода виртуализируемое представление поверх рефлексии. По умолчанию это просто пропустило бы все прямо из отражения, но у типов есть возможность выбрать пользовательские дескрипторы во время выполнения, позволяя им изменять или даже полностью заменять то, как они выглядят. ICustomTypeDescriptor является частью этого мира.

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

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

Конечно, ничего из этого не видно через обычные API отражения - VS и Blend имеют слой-обертку, который находится поверх отражения, чтобы все это работало.

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

Похоже, что в.NET 4.5, команда CLR решила, что, так как различные группы уже занимались этим, а другие группы хотели сделать больше (у команды MEF были аналогичные требования для времени выполнения с опцией отражения, необязательным). поведение при обсуждении), это было именно то, что должно быть встроено в среду выполнения.

Новая модель выглядит так: ReflectionContext Базовый класс - это абстрактный API, с помощью которого вы можете получить виртуализированную версию API отражения. Это обманчиво просто, потому что одна из основных идей заключается в том, что вам больше не нужны специализированные API, такие как система дескрипторов типов, если ваша единственная цель - получить виртуализируемый упаковщик поверх отражения - отражение теперь можно виртуализировать из коробки. Таким образом, вы можете написать такую ​​вещь

public static void ShowAllAttributes(Type t)
{
    foreach (Attribute attr in t.GetCustomAttributes(true))
    {
        Console.WriteLine(attr);
    }
}

Теперь вы всегда могли написать это, но до.NET 4.5 код, подобный этому, всегда работал с "реальной" информацией о типе, потому что он использует Reflection. Но благодаря контекстам рефлексии теперь можно обеспечить это виртуализированным Type, Итак, рассмотрим этот очень скучный тип:

class NoRealAttributes
{
}

Если вы просто проходите typeof(NoRealAttributes) к моему ShowAllAttributes метод, он ничего не распечатает. Но я могу написать (несколько надуманный) собственный контекст отражения:

class MyReflectionContext : CustomReflectionContext
{
    protected override IEnumerable<object> GetCustomAttributes(MemberInfo member, IEnumerable<object> declaredAttributes)
    {
        if (member == typeof(NoRealAttributes))
        {
            return new[] { new DefaultMemberAttribute("Foo") };
        }
        else
        {
            return base.GetCustomAttributes(member, declaredAttributes);
        }
    }
}

(Кстати, я думаю, что различие между CustomReflectionContext и его база, ReflectionContext является то, что последний определяет API для контекста виртуализируемого отражения, в то время как CustomReflectionContext добавляет несколько помощников, чтобы вам было проще реализовать такую ​​вещь.) И теперь я могу использовать это для предоставления виртуализированной версии Type для моего класса:

var ctx = new MyReflectionContext();
Type mapped = ctx.MapType(typeof(NoRealAttributes).GetTypeInfo());
ShowAllAttributes(mapped);

В этом коде mapped все еще относится к Type объект, так что все, кто знает, как использовать API отражения, сможет работать с ним, но теперь он сообщит о наличии атрибута, которого на самом деле нет. Конечно, Type абстрактно, поэтому у нас всегда есть что-то, что вытекает из этого, и если вы позвоните mapped.GetType() вы увидите, что это на самом деле System.Reflection.Context.Custom.CustomType а не System.RuntimeType вы бы обычно видели. И это CustomType объект принадлежит моему пользовательскому контексту, поэтому любые другие объекты API отражения, которые вы получаете через него (например, если вы написали mapped.Assembly.GetTypes()) вы также получите настраиваемые объекты, которые проходят через мой пользовательский контекст, который будет иметь возможность изменять все, что выходит.

Таким образом, код может перемещаться по системе типов, используя Type объект. Несмотря на то, что в таком коде используется обычный базовый API отражения, у меня теперь есть возможность настроить все, что из этого получится, если я сочту нужным.

Вы получите это виртуализированное представление, только если попросите его. Например, MEF в.NET 4.5 ищет пользовательский атрибут, указывающий, что он должен использовать пользовательский пользовательский контекст отражения, но в противном случае будет возвращаться к обычному отражению. (А в случае моего ShowAllAttributes метод, он использует все Type объект, который я выбираю для передачи - он не знает, получает ли он объект виртуализированного или "реального" типа.)

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

Другие вопросы по тегам