| File: TaskClassRegistration.cs | Web Access |
| Project: src\msbuild\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics.CodeAnalysis; using Microsoft.Build.Shared; namespace Microsoft.Build.Framework; /// <summary> /// A single entry in the <see cref="TaskClassRegistry"/>: how to construct a registered task and the /// reflected type metadata the engine binds its parameters against. /// </summary> /// <remarks> /// The <see cref="LoadedType"/> is built once. For the generic <see cref="TaskClassRegistry.Register{T}()"/> /// overload it is supplied eagerly at registration (where the task type is trim-rooted). For the /// <see cref="TaskClassRegistry.Register(string, Func{ITask})"/> overload - where only an untyped factory is /// known - it is built lazily from the first constructed instance's type. /// <para> /// The construction factory takes the <see cref="TaskEnvironment"/> the engine is running the task with, so a /// task that declares a constructor accepting a <see cref="TaskEnvironment"/> can receive it during /// construction (mirroring the reflective task-load path). Factories for tasks that do not need it simply /// ignore the argument. /// </para> /// </remarks> internal sealed class TaskClassRegistration { private readonly Func<TaskEnvironment, ITask> _createInstance; /// <summary> /// The reflected type metadata the engine binds the task's parameters against. For the generic, /// trim-rooted overload the value is known at registration; for the untyped /// <see cref="TaskClassRegistry.Register(string, Func{ITask})"/> overload it is computed on first use by /// probing a constructed instance's runtime type. Either way <see cref="Lazy{T}"/> memoizes it in a /// thread-safe way, so no field is ever observed in an unset state. /// </summary> private readonly Lazy<LoadedType> _loadedType; /// <summary> /// Creates a registration whose <see cref="LoadedType"/> is already known (the generic, trim-rooted path). /// </summary> internal TaskClassRegistration(Func<TaskEnvironment, ITask> createInstance, LoadedType loadedType) { _createInstance = createInstance; _loadedType = new Lazy<LoadedType>(() => loadedType); } /// <summary> /// Creates a registration backed only by an untyped factory; the <see cref="LoadedType"/> is computed /// lazily from the first probed instance's type. /// </summary> internal TaskClassRegistration(Func<ITask> factory) { _createInstance = _ => factory(); _loadedType = new Lazy<LoadedType>(() => CreateLoadedTypeFromFactory(factory)); } /// <summary> /// Constructs a new instance of the registered task. Reflection-free unless the registered task type /// declares a <see cref="TaskEnvironment"/> constructor (which is invoked via <see cref="Activator"/> over /// the trim-rooted type). The engine still assigns the task's <see cref="TaskEnvironment"/> property after /// construction. /// </summary> /// <param name="taskEnvironment">The environment supplied to a task that declares a <see cref="TaskEnvironment"/> constructor.</param> internal ITask CreateInstance(TaskEnvironment taskEnvironment) => _createInstance(taskEnvironment); /// <summary> /// Gets the reflected type metadata the engine uses to discover and bind the task's parameters. /// </summary> internal LoadedType GetLoadedType() => _loadedType.Value; /// <summary> /// Builds the <see cref="LoadedType"/> for the factory-only registration from a probe instance's type. /// </summary> [UnconditionalSuppressMessage( "Trimming", "IL2072:UnrecognizedReflectionPattern", Justification = "The Func<ITask> registration overload's task type is supplied by the host and is not statically known here. " + "The host is responsible for preserving the task type's public properties under trimming (for example by also registering it " + "through the generic RegisterTask<T> overload, which roots them, or via a TrimmerRootAssembly entry). The generic overload, " + "which the built-in tasks and most hosts use, supplies the LoadedType eagerly and is fully trim-safe.")] private static LoadedType CreateLoadedTypeFromFactory(Func<ITask> factory) => TaskClassRegistry.CreateLoadedType(factory().GetType()); }