/// <summary>Registers all event metric definitions defined by attributes on the provided object or Type,
        /// and registers metric instances where EventMetricInstanceName attribute is also found (with a live object).</summary>
        /// <param name="metricData">An object or Type defining event metrics via attributes on itself or on its base types or interfaces.</param>
        /// <remarks>
        ///     <para>
        ///         This call ensures that the reflection scan of all members looking for
        ///         attributes across the entire inheritance of an object instance or Type has been
        ///         done (e.g. outside of a critical path) so that the first call to <see cref="Write(object)">Write</see> will not have to do that work within a critical path.
        ///         Results are cached internally, so redundant calls to this method will not
        ///         repeat the scan for types already scanned (including as part of a different
        ///         top-level type).
        ///     </para>
        ///     <para>
        ///         If a live object is given (not just a Type) then the member(s) decorated with
        ///         an <see cref="EventMetricInstanceNameAttribute">EventMetricInstanceNameAttribute Class</see>
        ///         will be queried and used to also register an event metric instance with the
        ///         returned name
        ///     </para>
        ///     <para>If a Type is given instead of a live object, it can't be queried for instance
        ///     name(s) and will only register the event metric definitions. Metric instances will
        ///     still be created automatically as needed when Write is called.</para>
        /// </remarks>
        /// <seealso cref="Write(object)">Write Method</seealso>
        /// <exception caption="" cref="ArgumentNullException">Thrown if metricData is null.</exception>
        /// <exception caption="" cref="ArgumentException">The specified metricDataObjectType does not have an EventMetric attribute &lt;br /&gt;
        /// &lt;br /&gt;
        /// -or- &lt;br /&gt;
        /// &lt;br /&gt;
        /// The specified Type does not have a usable EventMetric attribute, so it can't be used to define an event metric.&lt;br /&gt;
        /// &lt;br /&gt;
        /// -or- &lt;br /&gt;
        /// &lt;br /&gt;
        /// The specified Type's EventMetric has an empty metric namespace which is not allowed, so no metric can be defined.&lt;br /&gt;
        /// &lt;br /&gt;
        /// -or- &lt;br /&gt;
        /// &lt;br /&gt;
        /// The specified Type's EventMetric has an empty metric category name which is not allowed, so no metric can be defined.&lt;br /&gt;
        /// &lt;br /&gt;
        /// -or- &lt;br /&gt;
        /// &lt;br /&gt;
        /// The specified Type's EventMetric has an empty metric counter name which is not allowed, so no metric can be defined.&lt;br /&gt;
        /// &lt;br /&gt;
        /// -or- &lt;br /&gt;
        /// &lt;br /&gt;
        /// The specified Type's EventMetric attribute's 3-part Key is already used for a metric definition which is not an event metric.</exception>
        /// <example>
        /// See the <see cref="EventMetric">EventMetric Class Overview</see> for an example.
        /// <code title="" description="" lang="neutral"></code></example>
        public static void Register(object metricData)
        {
            //we need a live object, not a null object or we'll fail
            if (metricData == null)
            {
                throw new ArgumentNullException(nameof(metricData));
            }

            // Register all of the event metric definitions it contains, object or Type:
            EventMetricDefinition[] definitions = EventMetricDefinition.RegisterAll(metricData);

            if ((metricData is Type) == false)
            {
                // They gave us a live object, not just a Type, so see if there are metric instances we can register.
                foreach (EventMetricDefinition definition in definitions)
                {
                    if (definition.IsBound && definition.NameBound)
                    {
                        string instanceName = definition.InvokeInstanceNameBinding(metricData);

                        if (instanceName != null) // null means it didn't find one, so we won't register an instance.
                        {
                            // An empty string (meaning the found value was null or empty) will be registered (same as null).
                            Register(definition, instanceName);
                        }
                    }
                }
            }
        }