| File: System\ComponentModel\Design\ServiceContainer.cs | Web Access |
| Project: src\runtime\src\libraries\System.ComponentModel.TypeConverter\src\System.ComponentModel.TypeConverter.csproj (System.ComponentModel.TypeConverter) |
// 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.Diagnostics; namespace System.ComponentModel.Design { /// <summary> /// This is a simple implementation of IServiceContainer. /// </summary> public class ServiceContainer : IServiceContainer, IDisposable { private ServiceCollection<object?>? _services; private readonly IServiceProvider? _parentProvider; private static readonly Type[] s_defaultServices = new Type[] { typeof(IServiceContainer), typeof(ServiceContainer) }; /// <summary> /// Creates a new service object container. /// </summary> public ServiceContainer() { } /// <summary> /// Creates a new service object container. /// </summary> public ServiceContainer(IServiceProvider? parentProvider) { _parentProvider = parentProvider; } /// <summary> /// Retrieves the parent service container, or null if there is no parent container. /// </summary> private IServiceContainer? Container { get => _parentProvider?.GetService(typeof(IServiceContainer)) as IServiceContainer; } /// <summary> /// This property returns the default services that are implemented directly on this IServiceContainer. /// the default implementation of this property is to return the IServiceContainer and ServiceContainer /// types. You may override this property and return your own types, modifying the default behavior /// of GetService. /// </summary> protected virtual Type[] DefaultServices => s_defaultServices; /// <summary> /// Our collection of services. The service collection is demand /// created here. /// </summary> private ServiceCollection<object?> Services => _services ??= new ServiceCollection<object?>(); /// <summary> /// Adds the given service to the service container. /// </summary> public void AddService(Type serviceType, object serviceInstance) { AddService(serviceType, serviceInstance, false); } /// <summary> /// Adds the given service to the service container. /// </summary> public virtual void AddService(Type serviceType, object serviceInstance, bool promote) { if (promote) { IServiceContainer? container = Container; if (container != null) { container.AddService(serviceType, serviceInstance, promote); return; } } // We're going to add this locally. Ensure that the service instance // is correct. // ArgumentNullException.ThrowIfNull(serviceType); ArgumentNullException.ThrowIfNull(serviceInstance); if (!(serviceInstance is ServiceCreatorCallback) && !serviceInstance.GetType().IsCOMObject && !serviceType.IsInstanceOfType(serviceInstance)) { throw new ArgumentException(SR.Format(SR.ErrorInvalidServiceInstance, serviceType.FullName)); } if (Services.ContainsKey(serviceType)) { throw new ArgumentException(SR.Format(SR.ErrorServiceExists, serviceType.FullName), nameof(serviceType)); } Services[serviceType] = serviceInstance; } /// <summary> /// Adds the given service to the service container. /// </summary> public void AddService(Type serviceType, ServiceCreatorCallback callback) { AddService(serviceType, callback, false); } /// <summary> /// Adds the given service to the service container. /// </summary> public virtual void AddService(Type serviceType, ServiceCreatorCallback callback, bool promote) { if (promote) { IServiceContainer? container = Container; if (container != null) { container.AddService(serviceType, callback, promote); return; } } // We're going to add this locally. Ensure that the service instance // is correct. // ArgumentNullException.ThrowIfNull(serviceType); ArgumentNullException.ThrowIfNull(callback); if (Services.ContainsKey(serviceType)) { throw new ArgumentException(SR.Format(SR.ErrorServiceExists, serviceType.FullName), nameof(serviceType)); } Services[serviceType] = callback; } /// <summary> /// Disposes this service container. This also walks all instantiated services within the container /// and disposes any that implement IDisposable, and clears the service list. /// </summary> public void Dispose() { Dispose(true); } /// <summary> /// Disposes this service container. This also walks all instantiated services within the container /// and disposes any that implement IDisposable, and clears the service list. /// </summary> protected virtual void Dispose(bool disposing) { if (disposing) { ServiceCollection<object?>? serviceCollection = _services; _services = null; if (serviceCollection != null) { foreach (object? o in serviceCollection.Values) { if (o is IDisposable) { ((IDisposable)o).Dispose(); } } } } } /// <summary> /// Retrieves the requested service. /// </summary> public virtual object? GetService(Type serviceType) { object? service = null; // Try locally. We first test for services we // implement and then look in our service collection. Type[] defaults = DefaultServices; for (int idx = 0; idx < defaults.Length; idx++) { if (serviceType != null && serviceType.IsEquivalentTo(defaults[idx])) { service = this; break; } } if (service == null && serviceType != null) { Services.TryGetValue(serviceType, out service); } // Is the service a creator delegate? if (service is ServiceCreatorCallback) { service = ((ServiceCreatorCallback)service)(this, serviceType!); if (service != null && !service.GetType().IsCOMObject && !serviceType!.IsInstanceOfType(service)) { // Callback passed us a bad service. NULL it, rather than throwing an exception. // Callers here do not need to be prepared to handle bad callback implementations. service = null; } // And replace the callback with our new service. Services[serviceType!] = service; } if (service == null && _parentProvider != null) { service = _parentProvider.GetService(serviceType!); } return service; } /// <summary> /// Removes the given service type from the service container. /// </summary> public void RemoveService(Type serviceType) { RemoveService(serviceType, false); } /// <summary> /// Removes the given service type from the service container. /// </summary> public virtual void RemoveService(Type serviceType, bool promote) { if (promote) { IServiceContainer? container = Container; if (container != null) { container.RemoveService(serviceType, promote); return; } } // We're going to remove this from our local list. ArgumentNullException.ThrowIfNull(serviceType); Services.Remove(serviceType); } /// <summary> /// Use this collection to store mapping from the Type of a service to the object that provides it in a way /// that is aware of embedded types. The comparer for this collection will call Type.IsEquivalentTo(...) /// instead of doing a reference comparison which will fail in type embedding scenarios. To speed the lookup /// performance we will use hash code of Type.FullName. /// </summary> /// <typeparam name="T"></typeparam> private sealed class ServiceCollection<T> : Dictionary<Type, T> { private static readonly EmbeddedTypeAwareTypeComparer s_serviceTypeComparer = new EmbeddedTypeAwareTypeComparer(); private sealed class EmbeddedTypeAwareTypeComparer : IEqualityComparer<Type> { public bool Equals(Type? x, Type? y) => x!.IsEquivalentTo(y); public int GetHashCode(Type obj) => obj.FullName!.GetHashCode(); } public ServiceCollection() : base(s_serviceTypeComparer) { } } } }