| File: System\ComponentModel\Design\InheritanceService.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms.Design\src\System.Windows.Forms.Design.csproj (System.Windows.Forms.Design) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel.Design.Serialization; using System.Reflection; using System.Runtime.CompilerServices; using System.Windows.Forms.Design; namespace System.ComponentModel.Design; /// <summary> /// Provides a set of methods for analyzing and identifying inherited components. /// </summary> public class InheritanceService : IInheritanceService, IDisposable { private Dictionary<IComponent, InheritanceAttribute> _inheritedComponents; // While we're adding an inherited component, we must be wary of components that the inherited component adds as a // result of being sited. These are treated as inherited as well. To track these, we keep track of the component // we're currently adding as well as it's inheritance attribute. During the add, we sync IComponentAdding events // and push in the component. private IComponent? _addingComponent; private InheritanceAttribute? _addingAttribute; /// <summary> /// Initializes a new instance of the <see cref="InheritanceService"/> class. /// </summary> public InheritanceService() { _inheritedComponents = []; } /// <summary> /// Disposes of the resources (other than memory) used by the <see cref="InheritanceService"/>. /// </summary> public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { if (disposing && _inheritedComponents is not null) { _inheritedComponents.Clear(); _inheritedComponents = null!; } } /// <summary> /// Adds inherited components to the <see cref="InheritanceService"/>. /// </summary> public void AddInheritedComponents(IComponent component, IContainer container) { AddInheritedComponents(component.GetType(), component, container); } /// <summary> /// Adds inherited components to the <see cref="InheritanceService"/>. /// </summary> protected virtual void AddInheritedComponents(Type? type, IComponent component, IContainer container) { // We get out now if this component type is not assignable from IComponent. We only walk down to the component level. if (type is null || !typeof(IComponent).IsAssignableFrom(type)) { return; } ISite? site = component.Site; IComponentChangeService? cs = null; INameCreationService? ncs = null; if (site is not null) { ncs = site.GetService<INameCreationService>(); cs = site.GetService<IComponentChangeService>(); cs?.ComponentAdding += OnComponentAdding; } try { while (type != typeof(object)) { Type reflect = TypeDescriptor.GetReflectionType(type); FieldInfo[] fields = reflect.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { string name = field.Name; // Get out now if this field is not assignable from IComponent. Type reflectionType = GetReflectionTypeFromTypeHelper(field.FieldType); if (!GetReflectionTypeFromTypeHelper(typeof(IComponent)).IsAssignableFrom(reflectionType)) { continue; } // Now check the attributes of the field and get out if it isn't something that can be inherited. Debug.Assert(!field.IsStatic, "Instance binding shouldn't have found this field"); // If the value of the field is null, then don't mess with it. If it wasn't assigned when our base // class was created then we can't really use it. object? value = field.GetValue(component); if (value is null) { continue; } // We've been fine up to this point looking at the field. Now, however, we must check to see if this // field has an AccessedThroughPropertyAttribute on it. If it does, then we must look for the property // and use its name and visibility for the remainder of the scan. Should any of this bail we just use the field. MemberInfo member = field; object[] fieldAttrs = field.GetCustomAttributes(typeof(AccessedThroughPropertyAttribute), false); if (fieldAttrs is not null && fieldAttrs.Length > 0) { Debug.Assert(fieldAttrs.Length == 1, "Non-inheritable attribute has more than one copy"); Debug.Assert(fieldAttrs[0] is AccessedThroughPropertyAttribute, "Reflection bug: GetCustomAttributes(type) didn't discriminate by type"); AccessedThroughPropertyAttribute propAttr = (AccessedThroughPropertyAttribute)fieldAttrs[0]; PropertyInfo? fieldProp = reflect.GetProperty(propAttr.PropertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Debug.Assert(fieldProp is not null, "Field declared with AccessedThroughPropertyAttribute has no associated property"); Debug.Assert(fieldProp.PropertyType == field.FieldType, "Field declared with AccessedThroughPropertyAttribute is associated with a property with a different return type."); if (fieldProp is not null && fieldProp.PropertyType == field.FieldType) { // If the property cannot be read, it is useless to us. if (!fieldProp.CanRead) { continue; } // We never access the set for the property, so we can concentrate on just the get method. member = fieldProp.GetGetMethod(true)!; Debug.Assert(member is not null, "GetGetMethod for property didn't return a method, but CanRead is true"); name = propAttr.PropertyName; } } // Add a user hook to add or remove members. The default hook here ignores all inherited private members. bool ignoreMember = IgnoreInheritedMember(member, component); // We now have an inherited member. Gather some information about it and then add it to our list. // We must always add to our list, but we may not want to add it to the container. That is up to // the IgnoreInheritedMember method. We add here because there are components in the world that, // when sited, add their children to the container too. That's fine, but we want to make sure we // account for them in the inheritance service too. InheritanceAttribute attr; Debug.Assert(value is IComponent, "Value of inherited field is not IComponent. How did this value get into the datatype?"); bool privateInherited = false; if (ignoreMember) { // If we are ignoring this member, then always mark it as private. The designer doesn't want it; // we only do this in case some other component adds this guy to the container. privateInherited = true; } else { if (member is FieldInfo fi) { privateInherited = fi.IsPrivate | fi.IsAssembly; } else if (member is MethodInfo mi) { privateInherited = mi.IsPrivate | mi.IsAssembly; } } if (privateInherited) { attr = InheritanceAttribute.InheritedReadOnly; } else { attr = InheritanceAttribute.Inherited; } // We only get values via IComponent Keys and don't expose any other behaviors. // So only use IComponent as a key. if (value is IComponent compValue) { bool notPresent = !_inheritedComponents.ContainsKey(compValue); _inheritedComponents[compValue] = attr; if (!ignoreMember && notPresent) { try { _addingComponent = compValue; _addingAttribute = attr; // Lets make sure this is a valid name if (ncs is null || ncs.IsValidName(name)) { try { container.Add(compValue, name); } catch { // We do not always control the base components, and there could be a lot of rogue // base components. If there are exceptions when adding them, lets just ignore and continue. } } } finally { _addingComponent = null; _addingAttribute = null; } } } } type = type.BaseType!; } } finally { cs?.ComponentAdding -= OnComponentAdding; } } /// <summary> /// Indicates the inherited members to ignore. /// </summary> protected virtual bool IgnoreInheritedMember(MemberInfo member, IComponent? component) { // Our default implementation ignores all private or assembly members. if (member is FieldInfo field) { return field.IsPrivate || field.IsAssembly; } else if (member is MethodInfo method) { return method.IsPrivate || method.IsAssembly; } Debug.Fail("Unknown member type passed to IgnoreInheritedMember"); return true; } /// <summary> /// Gets the inheritance attribute of the specified component. /// </summary> public InheritanceAttribute GetInheritanceAttribute(IComponent component) => _inheritedComponents.TryGetValue(component, out InheritanceAttribute? attr) ? attr : InheritanceAttribute.Default; private void OnComponentAdding(object? sender, ComponentEventArgs ce) { if (_addingComponent is not null && _addingComponent != ce.Component) { _inheritedComponents[ce.Component!] = InheritanceAttribute.InheritedReadOnly; // If this component is being added to a nested container of addingComponent, it should get the same inheritance level. if (sender is INestedContainer nested && nested.Owner == _addingComponent) { _inheritedComponents[ce.Component!] = _addingAttribute!; } } } [return: NotNullIfNotNull(nameof(type))] private static Type? GetReflectionTypeFromTypeHelper(Type? type) { if (type is not null) { TypeDescriptionProvider? targetProvider = GetTargetFrameworkProviderForType(type); if (targetProvider is not null) { if (targetProvider.IsSupportedType(type)) { return targetProvider.GetReflectionType(type); } } } return type; } private static TypeDescriptionProvider? GetTargetFrameworkProviderForType(Type type) { return DocumentDesigner.s_manager?.GetService<TypeDescriptionProviderService>()?.GetProvider(type); } }