|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Reflection.Context.Custom;
using System.Reflection.Context.Projection;
using System.Reflection.Context.Virtual;
namespace System.Reflection.Context
{
internal sealed class IdentityReflectionContext : ReflectionContext
{
public override Assembly MapAssembly(Assembly assembly) { return assembly; }
public override TypeInfo MapType(TypeInfo type) { return type; }
}
/// <summary>
/// Represents a customizable reflection context.
/// </summary>
/// <remarks><format type="text/markdown"><![CDATA[
///
/// <xref:System.Reflection.Context.CustomReflectionContext> provides a way for you to add or remove custom attributes from reflection objects, or add dummy
/// properties to those objects, without re-implementing the complete reflection model. The default <xref:System.Reflection.Context.CustomReflectionContext>
/// simply wraps reflection objects without making any changes, but by subclassing and overriding the relevant methods, you can add, remove, or change the attributes
/// that apply to any reflected parameter or member, or add new properties to a reflected type.
///
/// For example, suppose that your code follows the convention of applying a particular attribute to factory methods, but you're now required to work with third-party
/// code that lacks attributes. You can use <xref:System.Reflection.Context.CustomReflectionContext> to specify a rule for identifying the objects that should have attributes
/// and to supply the objects with those attributes when they're viewed from your code.
///
/// To use <xref:System.Reflection.Context.CustomReflectionContext> effectively, the code that uses the reflected objects must support the notion of specifying
/// a reflection context, instead of assuming that all reflected objects are associated with the runtime reflection context. Many reflection methods in .NET provide
/// a <xref:System.Reflection.ReflectionContext> parameter for this purpose.
///
/// To modify the attributes that are applied to a reflected parameter or member, override the
/// <xref:System.Reflection.Context.CustomReflectionContext.GetCustomAttributes(System.Reflection.ParameterInfo,System.Collections.Generic.IEnumerable{System.Object})>
/// or <xref:System.Reflection.Context.CustomReflectionContext.GetCustomAttributes(System.Reflection.MemberInfo,System.Collections.Generic.IEnumerable{System.Object})> method.
/// These methods take the reflected object and the list of attributes under its current reflection context, and return the list of attributes it should have under
/// the custom reflection context.
///
/// > [!WARNING]
/// > <xref:System.Reflection.Context.CustomReflectionContext> overrides should not query attributes on the provided <xref:System.Reflection.MemberInfo> or <xref:System.Reflection.ParameterInfo>
/// > by calling <xref:System.Reflection.CustomAttributeExtensions.GetCustomAttributes*>; use the `declaredAttributes` sequence passed to the <xref:System.Reflection.Context.CustomReflectionContext.GetCustomAttributes*> overloads instead.
///
/// To add properties to a reflected type, override the <xref:System.Reflection.Context.CustomReflectionContext.AddProperties*> method. The method accepts a parameter that
/// specifies the reflected type, and returns a list of additional properties. You should use the <xref:System.Reflection.Context.CustomReflectionContext.CreateProperty*>
/// method to create property objects to return. You can specify delegates when creating the property that serve as the property accessor, and you can omit one of
/// the accessors to create a read-only or write-only property. Note that such dummy properties have no metadata or Common Intermediate Language (CIL) backing.
///
/// > [!WARNING]
/// > - Be cautious about equality among reflected objects when you work with reflection contexts, because objects might represent the same reflected object in multiple
/// > contexts. You can use the <xref:System.Reflection.Context.CustomReflectionContext.MapType*> method to obtain a particular reflection context's version of a reflected object.
/// > - A <xref:System.Reflection.Context.CustomReflectionContext> object alters the attributes returned by a particular reflection object, such as those obtained by the
/// > <xref:System.Reflection.MemberInfo.GetCustomAttributes*> method. It doesn't alter the custom attribute data returned by the <xref:System.Reflection.MemberInfo.GetCustomAttributesData*>
/// > method, and these two lists won't match when you use a custom reflection context.
/// ]]></format></remarks>
/// <example>
/// The following example demonstrates how to subclass <see cref="CustomReflectionContext" />
/// to add a custom attribute to all the members of a given type whose names begin with "To".
/// <code lang="cs" source="../../../../tests/CustomReflectionContext.Examples.cs" region="Snippet1" />
/// <code lang="cs" source="../../../../tests/CustomReflectionContext.Examples.cs" region="Snippet2" />
/// </example>
public abstract partial class CustomReflectionContext : ReflectionContext
{
private readonly ReflectionContextProjector _projector;
/// <summary>
/// Initializes a new instance of the <see cref="CustomReflectionContext"/> class.
/// </summary>
protected CustomReflectionContext() : this(new IdentityReflectionContext()) { }
/// <summary>
/// Initializes a new instance of the <see cref="CustomReflectionContext"/> class with the specified reflection context as a base.
/// </summary>
/// <param name="source">The reflection context to use as a base.</param>
protected CustomReflectionContext(ReflectionContext source)
{
ArgumentNullException.ThrowIfNull(source);
SourceContext = source;
_projector = new ReflectionContextProjector(this);
}
/// <summary>
/// Gets the representation, in this reflection context, of an assembly that is represented by an object from another reflection context.
/// </summary>
/// <param name="assembly">The external representation of the assembly to represent in this context.</param>
/// <returns>The representation of the assembly in this reflection context.</returns>
public override Assembly MapAssembly(Assembly assembly)
{
ArgumentNullException.ThrowIfNull(assembly);
return _projector.ProjectAssemblyIfNeeded(assembly);
}
/// <summary>
/// Gets the representation, in this reflection context, of a type represented by an object from another reflection context.
/// </summary>
/// <param name="type">The external representation of the type to represent in this context.</param>
/// <returns>The representation of the type in this reflection context.</returns>
public override TypeInfo MapType(TypeInfo type)
{
ArgumentNullException.ThrowIfNull(type);
return _projector.ProjectTypeIfNeeded(type);
}
/// <summary>
/// When overridden in a derived class, provides a list of custom attributes for the specified member, as represented in this reflection context.
/// </summary>
/// <param name="member">The member whose custom attributes will be returned.</param>
/// <param name="declaredAttributes">A collection of the member's attributes in its current context.</param>
/// <returns>A collection that represents the custom attributes of the specified member in this reflection context.</returns>
protected virtual IEnumerable<object> GetCustomAttributes(MemberInfo member, IEnumerable<object> declaredAttributes)
{
return declaredAttributes;
}
/// <summary>
/// When overridden in a derived class, provides a list of custom attributes for the specified parameter, as represented in this reflection context.
/// </summary>
/// <param name="parameter">The parameter whose custom attributes will be returned.</param>
/// <param name="declaredAttributes">A collection of the parameter's attributes in its current context.</param>
/// <returns>A collection that represents the custom attributes of the specified parameter in this reflection context.</returns>
protected virtual IEnumerable<object> GetCustomAttributes(ParameterInfo parameter, IEnumerable<object> declaredAttributes)
{
return declaredAttributes;
}
// The default implementation of GetProperties: just return an empty list.
/// <summary>
/// When overridden in a derived class, provides a collection of additional properties for the specified type, as represented in this reflection context.
/// </summary>
/// <param name="type">The type to add properties to.</param>
/// <returns>A collection of additional properties for the specified type.</returns>
/// <remarks>
/// Override this method to specify which properties should be added to a given type. To create the properties, use the <see cref="CreateProperty(Type, string, Func{object, object?}?, Action{object, object?}?)"/> method.
/// </remarks>
protected virtual IEnumerable<PropertyInfo> AddProperties(Type type)
{
// return an empty enumeration
yield break;
}
/// <summary>
/// Creates an object that represents a property to be added to a type, to be used with the <see cref="AddProperties(Type)"/> method.
/// </summary>
/// <param name="propertyType">The type of the property to create.</param>
/// <param name="name">The name of the property to create.</param>
/// <param name="getter">A delegate that represents the property's <see langword="get"/> accessor.</param>
/// <param name="setter">A delegate that represents the property's <see langword="set"/> accessor.</param>
/// <returns>An object that represents the property.</returns>
/// <remarks>
/// Objects that are returned by this method are not complete <see cref="PropertyInfo"/> objects, and should be used only in the context of the <see cref="AddProperties(Type)"/> method.
/// </remarks>
protected PropertyInfo CreateProperty(
Type propertyType,
string name,
Func<object, object?>? getter,
Action<object, object?>? setter)
{
return new VirtualPropertyInfo(
name,
propertyType,
getter,
setter,
null,
null,
null,
this);
}
/// <summary>
/// Creates an object that represents a property to be added to a type, to be used with the <see cref="AddProperties(Type)"/> method and using the specified custom attributes.
/// </summary>
/// <param name="propertyType">The type of the property to create.</param>
/// <param name="name">The name of the property to create.</param>
/// <param name="getter">A delegate that represents the property's <see langword="get"/> accessor.</param>
/// <param name="setter">A delegate that represents the property's <see langword="set"/> accessor.</param>
/// <param name="propertyCustomAttributes">A collection of custom attributes to apply to the property.</param>
/// <param name="getterCustomAttributes">A collection of custom attributes to apply to the property's <see langword="get"/> accessor.</param>
/// <param name="setterCustomAttributes">A collection of custom attributes to apply to the property's <see langword="set"/> accessor.</param>
/// <returns>An object that represents the property.</returns>
/// <remarks>
/// Objects that are returned by this method are not complete <see cref="PropertyInfo"/> objects, and should be used only in the context of the <see cref="AddProperties(Type)"/> method.
/// </remarks>
protected PropertyInfo CreateProperty(
Type propertyType,
string name,
Func<object, object?>? getter,
Action<object, object?>? setter,
IEnumerable<Attribute>? propertyCustomAttributes,
IEnumerable<Attribute>? getterCustomAttributes,
IEnumerable<Attribute>? setterCustomAttributes)
{
return new VirtualPropertyInfo(
name,
propertyType,
getter,
setter,
propertyCustomAttributes,
getterCustomAttributes,
setterCustomAttributes,
this);
}
internal IEnumerable<PropertyInfo> GetNewPropertiesForType(CustomType type)
{
// We don't support adding properties on these types.
if (type.IsInterface || type.IsGenericParameter || type.HasElementType)
yield break;
// Passing in the underlying type.
IEnumerable<PropertyInfo> newProperties = AddProperties(type.UnderlyingType);
// Setting DeclaringType on the user provided virtual properties.
foreach (PropertyInfo prop in newProperties)
{
if (prop == null)
throw new InvalidOperationException(SR.InvalidOperation_AddNullProperty);
VirtualPropertyBase? vp = prop as VirtualPropertyBase;
if (vp == null || vp.ReflectionContext != this)
throw new InvalidOperationException(SR.InvalidOperation_AddPropertyDifferentContext);
if (vp.DeclaringType == null)
vp.SetDeclaringType(type);
else if (!vp.DeclaringType.Equals(type))
throw new InvalidOperationException(SR.InvalidOperation_AddPropertyDifferentType);
yield return prop;
}
}
internal IEnumerable<object> GetCustomAttributesOnMember(MemberInfo member, IEnumerable<object> declaredAttributes, Type attributeFilterType)
{
IEnumerable<object> attributes = GetCustomAttributes(member, declaredAttributes);
return AttributeUtils.FilterCustomAttributes(attributes, attributeFilterType);
}
internal IEnumerable<object> GetCustomAttributesOnParameter(ParameterInfo parameter, IEnumerable<object> declaredAttributes, Type attributeFilterType)
{
IEnumerable<object> attributes = GetCustomAttributes(parameter, declaredAttributes);
return AttributeUtils.FilterCustomAttributes(attributes, attributeFilterType);
}
internal Projector Projector
{
get
{
return _projector;
}
}
internal ReflectionContext SourceContext { get; }
}
}
|