File: DependencyService.cs
Web Access
Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls)
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Microsoft.Maui.Controls.Internals;
 
namespace Microsoft.Maui.Controls
{
	/// <include file="../../docs/Microsoft.Maui.Controls/DependencyService.xml" path="Type[@FullName='Microsoft.Maui.Controls.DependencyService']/Docs/*" />
	public static class DependencyService
	{
		static bool s_initialized;
 
		static readonly object s_dependencyLock = new object();
		static readonly object s_initializeLock = new object();
 
		static readonly List<DependencyType> DependencyTypes = new List<DependencyType>();
		static readonly Dictionary<Type, DependencyData> DependencyImplementations = new Dictionary<Type, DependencyData>();
 
		public static T Resolve<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(DependencyFetchTarget fallbackFetchTarget = DependencyFetchTarget.GlobalInstance) where T : class
		{
			var result = DependencyResolver.Resolve(typeof(T)) as T;
 
			return result ?? Get<T>(fallbackFetchTarget);
		}
 
		public static T Get<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(DependencyFetchTarget fetchTarget = DependencyFetchTarget.GlobalInstance) where T : class
		{
			Initialize();
 
			DependencyData dependencyImplementation;
			lock (s_dependencyLock)
			{
				Type targetType = typeof(T);
				if (!DependencyImplementations.TryGetValue(targetType, out dependencyImplementation))
				{
					Type implementor = FindImplementor(targetType);
					DependencyImplementations[targetType] = (dependencyImplementation = implementor != null ? new DependencyData { ImplementorType = implementor } : null);
				}
			}
 
			if (dependencyImplementation == null)
				return null;
 
			if (fetchTarget == DependencyFetchTarget.GlobalInstance)
			{
				if (dependencyImplementation.GlobalInstance == null)
				{
					lock (dependencyImplementation)
					{
						if (dependencyImplementation.GlobalInstance == null)
						{
							dependencyImplementation.GlobalInstance = Activator.CreateInstance(dependencyImplementation.ImplementorType);
						}
					}
				}
				return (T)dependencyImplementation.GlobalInstance;
			}
			return (T)Activator.CreateInstance(dependencyImplementation.ImplementorType);
		}
 
		public static void Register<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T>() where T : class
		{
			Type type = typeof(T);
			AddDependencyTypeIfNeeded(type);
		}
 
		public static void Register<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TImpl>() where T : class where TImpl : class, T
		{
			Type targetType = typeof(T);
			Type implementorType = typeof(TImpl);
			AddDependencyTypeIfNeeded(targetType);
 
			lock (s_dependencyLock)
				DependencyImplementations[targetType] = new DependencyData { ImplementorType = implementorType };
		}
 
		public static void RegisterSingleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T>(T instance) where T : class
		{
			Type targetType = typeof(T);
			Type implementorType = typeof(T);
			AddDependencyTypeIfNeeded(targetType);
 
			lock (s_dependencyLock)
				DependencyImplementations[targetType] = new DependencyData { ImplementorType = implementorType, GlobalInstance = instance };
		}
 
		static void AddDependencyTypeIfNeeded(
			[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type)
		{
			if (!DependencyTypes.Any(t => t.Type == type))
				DependencyTypes.Add(new DependencyType { Type = type });
		}
 
		[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
		static Type FindImplementor(Type target) =>
			DependencyTypes.FirstOrDefault(t => target.IsAssignableFrom(t.Type)).Type;
 
		// Once we get essentials/cg converted to using startup.cs
		// we will delete the initialize code from here and just use
		// explicit assembly registration via startup code
		internal static void SetToInitialized()
		{
			s_initialized = true;
		}
 
		static void Initialize()
		{
			if (s_initialized)
				return;
 
			lock (s_initializeLock)
			{
				if (s_initialized)
					return;
 
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				if (Internals.Registrar.ExtraAssemblies != null)
				{
					assemblies = assemblies.Union(Internals.Registrar.ExtraAssemblies).ToArray();
				}
 
				Initialize(assemblies);
			}
		}
 
		public static void Register(Assembly[] assemblies)
		{
			lock (s_initializeLock)
			{
				// Don't use LINQ for performance reasons
				// Naive implementation can easily take over a second to run
				foreach (Assembly assembly in assemblies)
				{
					object[] attributes = assembly.GetCustomAttributesSafe(typeof(DependencyAttribute));
					if (attributes == null)
						continue;
 
					for (int i = 0; i < attributes.Length; i++)
					{
						DependencyAttribute attribute = (DependencyAttribute)attributes[i];
						AddDependencyTypeIfNeeded(attribute.Implementor);
					}
				}
			}
		}
 
		internal static void Initialize(Assembly[] assemblies)
		{
			if (s_initialized)
				return;
 
			lock (s_initializeLock)
			{
				if (s_initialized)
					return;
 
				Register(assemblies);
 
				s_initialized = true;
			}
		}
 
		class DependencyData
		{
			public object GlobalInstance { get; set; }
 
			[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
			public Type ImplementorType { get; set; }
		}
 
		struct DependencyType
		{
			[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
			public Type Type { get; set; }
		}
	}
}