File: System\ComponentModel\Composition\Primitives\Export.cs
Web Access
Project: src\src\libraries\System.ComponentModel.Composition\src\System.ComponentModel.Composition.csproj (System.ComponentModel.Composition)
// 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;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.Internal;
 
namespace System.ComponentModel.Composition.Primitives
{
    /// <summary>
    ///     Represents an export. That is, a type that is made up of a delay-created exported value
    ///     and metadata that describes that object.
    /// </summary>
    public class Export
    {
        private readonly ExportDefinition? _definition;
        private readonly Func<object?>? _exportedValueGetter;
        private static readonly object _EmptyValue = new object();
        private volatile object? _exportedValue = Export._EmptyValue;
 
        /// <summary>
        ///     Initializes a new instance of the <see cref="Export"/> class.
        /// </summary>
        /// <remarks>
        ///     <note type="inheritinfo">
        ///         Derived types calling this constructor must override <see cref="Definition"/>
        ///         and <see cref="GetExportedValueCore"/>.
        ///     </note>
        /// </remarks>
        protected Export()
        {
        }
 
        /// <summary>
        ///     Initializes a new instance of the <see cref="Export"/> class
        ///     with the specified contract name and exported value getter.
        /// </summary>
        /// <param name="contractName">
        ///     A <see cref="string"/> containing the contract name of the
        ///     <see cref="Export"/>.
        /// </param>
        /// <param name="exportedValueGetter">
        ///     A <see cref="Func{T}"/> that is called to create the exported value of the
        ///     <see cref="Export"/>. This allows the creation of the object to be delayed
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="contractName"/> is <see langword="null"/>.
        ///     <para>
        ///         -or-
        ///     </para>
        ///     <paramref name="exportedValueGetter"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///     <paramref name="contractName"/> is an empty string ("").
        /// </exception>
        public Export(string contractName, Func<object?> exportedValueGetter)
            : this(new ExportDefinition(contractName, (IDictionary<string, object?>?)null), exportedValueGetter)
        {
        }
 
        /// <summary>
        ///     Initializes a new instance of the <see cref="Export"/> class
        ///     with the specified contract name, metadata and exported value getter.
        /// </summary>
        /// <param name="contractName">
        ///     A <see cref="string"/> containing the contract name of the
        ///     <see cref="Export"/>.
        /// </param>
        /// <param name="metadata">
        ///     An <see cref="IDictionary{TKey, TValue}"/> containing the metadata of the
        ///     <see cref="Export"/>; or <see langword="null"/> to set the
        ///     <see cref="Metadata"/> property to an empty, read-only
        ///     <see cref="IDictionary{TKey, TValue}"/>.
        /// </param>
        /// <param name="exportedValueGetter">
        ///     A <see cref="Func{T}"/> that is called to create the exported value of the
        ///     <see cref="Export"/>. This allows the creation of the object to be delayed.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="contractName"/> is <see langword="null"/>.
        ///     <para>
        ///         -or-
        ///     </para>
        ///     <paramref name="exportedValueGetter"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///     <paramref name="contractName"/> is an empty string ("").
        /// </exception>
        public Export(string contractName, IDictionary<string, object?>? metadata, Func<object?> exportedValueGetter)
            : this(new ExportDefinition(contractName, metadata), exportedValueGetter)
        {
        }
 
        /// <summary>
        ///     Initializes a new instance of the <see cref="Export"/> class
        ///     with the specified export definition and exported value getter.
        /// </summary>
        /// <param name="definition">
        ///     An <see cref="ExportDefinition"/> that describes the contract that the
        ///     <see cref="Export"/> satisfies.
        /// </param>
        /// <param name="exportedValueGetter">
        ///     A <see cref="Func{T}"/> that is called to create the exported value of the
        ///     <see cref="Export"/>. This allows the creation of the object to be delayed.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="definition"/> is <see langword="null"/>.
        ///     <para>
        ///         -or-
        ///     </para>
        ///     <paramref name="exportedValueGetter"/> is <see langword="null"/>.
        /// </exception>
        public Export(ExportDefinition definition, Func<object?> exportedValueGetter)
        {
            Requires.NotNull(definition, nameof(definition));
            Requires.NotNull(exportedValueGetter, nameof(exportedValueGetter));
 
            _definition = definition;
            _exportedValueGetter = exportedValueGetter;
        }
 
        /// <summary>
        ///     Gets the definition that describes the contract that the export satisfies.
        /// </summary>
        /// <value>
        ///     An <see cref="ExportDefinition"/> that describes the contract that
        ///     the <see cref="Export"/> satisfies.
        /// </value>
        /// <exception cref="NotImplementedException">
        ///     This property was not overridden by a derived class.
        /// </exception>
        /// <remarks>
        ///     <note type="inheritinfo">
        ///         Overriders of this property should never return
        ///         <see langword="null"/>.
        ///     </note>
        /// </remarks>
        public virtual ExportDefinition Definition
        {
            get
            {
                if (_definition != null)
                {
                    return _definition;
                }
 
                throw ExceptionBuilder.CreateNotOverriddenByDerived("Definition");
            }
        }
 
        /// <summary>
        ///     Gets the metadata of the export.
        /// </summary>
        /// <value>
        ///     An <see cref="IDictionary{TKey, TValue}"/> containing the metadata of the
        ///     <see cref="Export"/>.
        /// </value>
        /// <exception cref="NotImplementedException">
        ///     The <see cref="Definition"/> property was not overridden by a derived class.
        /// </exception>
        /// <remarks>
        ///     <para>
        ///         This property returns the value of <see cref="ExportDefinition.Metadata"/>
        ///         of the <see cref="Definition"/> property.
        ///     </para>
        /// </remarks>
        public IDictionary<string, object?> Metadata
        {
            get
            {
                Debug.Assert(Definition.Metadata != null);
 
                return Definition.Metadata;
            }
        }
 
        /// <summary>
        ///     Returns the exported value of the export.
        /// </summary>
        /// <returns>
        ///     The exported <see cref="object"/> of the <see cref="Export"/>.
        /// </returns>
        /// <exception cref="CompositionException">
        ///     An error occurred during composition. <see cref="CompositionException.Errors"/> will
        ///     contain a collection of errors that occurred.
        /// </exception>
        /// <exception cref="CompositionContractMismatchException">
        ///     The current instance is an instance of <see cref="Lazy{T}"/> and the underlying
        ///     exported value cannot be cast to <c>T</c>.
        /// </exception>
        /// <exception cref="NotImplementedException">
        ///     The <see cref="GetExportedValueCore"/> method was not overridden by a derived class.
        /// </exception>
        public object? Value
        {
            get
            {
                // NOTE : the logic below guarantees that the value will be set exactly once. It DOES NOT, however, guarantee that GetExportedValueCore() will be executed
                // only once, as locking would be required for that. The said locking is problematic, as we can't reliable call 3rd party code under a lock.
                if (_exportedValue == Export._EmptyValue)
                {
                    object? exportedValue = GetExportedValueCore();
                    Interlocked.CompareExchange(ref _exportedValue, exportedValue, Export._EmptyValue);
                }
 
                return _exportedValue;
            }
        }
 
        /// <summary>
        ///     Returns the exported value of the export.
        /// </summary>
        /// <returns>
        ///     The exported <see cref="object"/> of the <see cref="Export"/>.
        /// </returns>
        /// <exception cref="CompositionException">
        ///     An error occurred during composition. <see cref="CompositionException.Errors"/> will
        ///     contain a collection of errors that occurred.
        /// </exception>
        /// <exception cref="CompositionContractMismatchException">
        ///     The current instance is an instance of <see cref="Lazy{T}"/> and the underlying
        ///     exported value cannot be cast to <c>T</c>.
        /// </exception>
        /// <exception cref="NotImplementedException">
        ///     The method was not overridden by a derived class.
        /// </exception>
        /// <remarks>
        ///     <note type="inheritinfo">
        ///         Overriders of this method should never return
        ///         <see langword="null"/>.
        ///     </note>
        /// </remarks>
        protected virtual object? GetExportedValueCore()
        {
            if (_exportedValueGetter != null)
            {
                return _exportedValueGetter.Invoke();
            }
 
            throw ExceptionBuilder.CreateNotOverriddenByDerived("GetExportedValueCore");
        }
    }
}