| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Diagnostics;
- using System.Globalization;
- using System.Runtime.ExceptionServices;
- using System.Runtime.Serialization;
- using System.Security;
- using System.Text;
- using System.Threading;
- namespace System
- {
- /// <summary>Represents one or more errors that occur during application execution.</summary>
- /// <remarks>
- /// <see cref="AggregateException"/> is used to consolidate multiple failures into a single, throwable
- /// exception object.
- /// </remarks>
- [Serializable]
- [DebuggerDisplay("Count = {InnerExceptionCount}")]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public class AggregateException : Exception
- {
- private ReadOnlyCollection<Exception> m_innerExceptions; // Complete set of exceptions. Do not rename (binary serialization)
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class.
- /// </summary>
- public AggregateException()
- : base(SR.AggregateException_ctor_DefaultMessage)
- {
- m_innerExceptions = new ReadOnlyCollection<Exception>(Array.Empty<Exception>());
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class with
- /// a specified error message.
- /// </summary>
- /// <param name="message">The error message that explains the reason for the exception.</param>
- public AggregateException(string message)
- : base(message)
- {
- m_innerExceptions = new ReadOnlyCollection<Exception>(Array.Empty<Exception>());
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error
- /// message and a reference to the inner exception that is the cause of this exception.
- /// </summary>
- /// <param name="message">The error message that explains the reason for the exception.</param>
- /// <param name="innerException">The exception that is the cause of the current exception.</param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerException"/> argument
- /// is null.</exception>
- public AggregateException(string message, Exception innerException)
- : base(message, innerException)
- {
- if (innerException == null)
- {
- throw new ArgumentNullException(nameof(innerException));
- }
- m_innerExceptions = new ReadOnlyCollection<Exception>(new Exception[] { innerException });
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class with
- /// references to the inner exceptions that are the cause of this exception.
- /// </summary>
- /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
- /// is null.</exception>
- /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
- /// null.</exception>
- public AggregateException(IEnumerable<Exception> innerExceptions) :
- this(SR.AggregateException_ctor_DefaultMessage, innerExceptions)
- {
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class with
- /// references to the inner exceptions that are the cause of this exception.
- /// </summary>
- /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
- /// is null.</exception>
- /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
- /// null.</exception>
- public AggregateException(params Exception[] innerExceptions) :
- this(SR.AggregateException_ctor_DefaultMessage, innerExceptions)
- {
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error
- /// message and references to the inner exceptions that are the cause of this exception.
- /// </summary>
- /// <param name="message">The error message that explains the reason for the exception.</param>
- /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
- /// is null.</exception>
- /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
- /// null.</exception>
- public AggregateException(string message, IEnumerable<Exception> innerExceptions)
- // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor). If it's null, just pass along
- // null typed correctly. Otherwise, create an IList from the enumerable and pass that along.
- : this(message, innerExceptions as IList<Exception> ?? (innerExceptions == null ? (List<Exception>)null : new List<Exception>(innerExceptions)))
- {
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error
- /// message and references to the inner exceptions that are the cause of this exception.
- /// </summary>
- /// <param name="message">The error message that explains the reason for the exception.</param>
- /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
- /// is null.</exception>
- /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
- /// null.</exception>
- public AggregateException(string message, params Exception[] innerExceptions) :
- this(message, (IList<Exception>)innerExceptions)
- {
- }
- /// <summary>
- /// Allocates a new aggregate exception with the specified message and list of inner exceptions.
- /// </summary>
- /// <param name="message">The error message that explains the reason for the exception.</param>
- /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
- /// is null.</exception>
- /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
- /// null.</exception>
- private AggregateException(string message, IList<Exception> innerExceptions)
- : base(message, innerExceptions != null && innerExceptions.Count > 0 ? innerExceptions[0] : null)
- {
- if (innerExceptions == null)
- {
- throw new ArgumentNullException(nameof(innerExceptions));
- }
- // Copy exceptions to our internal array and validate them. We must copy them,
- // because we're going to put them into a ReadOnlyCollection which simply reuses
- // the list passed in to it. We don't want callers subsequently mutating.
- Exception[] exceptionsCopy = new Exception[innerExceptions.Count];
- for (int i = 0; i < exceptionsCopy.Length; i++)
- {
- exceptionsCopy[i] = innerExceptions[i];
- if (exceptionsCopy[i] == null)
- {
- throw new ArgumentException(SR.AggregateException_ctor_InnerExceptionNull);
- }
- }
- m_innerExceptions = new ReadOnlyCollection<Exception>(exceptionsCopy);
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class with
- /// references to the inner exception dispatch info objects that represent the cause of this exception.
- /// </summary>
- /// <param name="innerExceptionInfos">
- /// Information about the exceptions that are the cause of the current exception.
- /// </param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptionInfos"/> argument
- /// is null.</exception>
- /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptionInfos"/> is
- /// null.</exception>
- internal AggregateException(IEnumerable<ExceptionDispatchInfo> innerExceptionInfos) :
- this(SR.AggregateException_ctor_DefaultMessage, innerExceptionInfos)
- {
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error
- /// message and references to the inner exception dispatch info objects that represent the cause of
- /// this exception.
- /// </summary>
- /// <param name="message">The error message that explains the reason for the exception.</param>
- /// <param name="innerExceptionInfos">
- /// Information about the exceptions that are the cause of the current exception.
- /// </param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptionInfos"/> argument
- /// is null.</exception>
- /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptionInfos"/> is
- /// null.</exception>
- internal AggregateException(string message, IEnumerable<ExceptionDispatchInfo> innerExceptionInfos)
- // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor). If it's null, just pass along
- // null typed correctly. Otherwise, create an IList from the enumerable and pass that along.
- : this(message, innerExceptionInfos as IList<ExceptionDispatchInfo> ??
- (innerExceptionInfos == null ?
- (List<ExceptionDispatchInfo>)null :
- new List<ExceptionDispatchInfo>(innerExceptionInfos)))
- {
- }
- /// <summary>
- /// Allocates a new aggregate exception with the specified message and list of inner
- /// exception dispatch info objects.
- /// </summary>
- /// <param name="message">The error message that explains the reason for the exception.</param>
- /// <param name="innerExceptionInfos">
- /// Information about the exceptions that are the cause of the current exception.
- /// </param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptionInfos"/> argument
- /// is null.</exception>
- /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptionInfos"/> is
- /// null.</exception>
- private AggregateException(string message, IList<ExceptionDispatchInfo> innerExceptionInfos)
- : base(message, innerExceptionInfos != null && innerExceptionInfos.Count > 0 && innerExceptionInfos[0] != null ?
- innerExceptionInfos[0].SourceException : null)
- {
- if (innerExceptionInfos == null)
- {
- throw new ArgumentNullException(nameof(innerExceptionInfos));
- }
- // Copy exceptions to our internal array and validate them. We must copy them,
- // because we're going to put them into a ReadOnlyCollection which simply reuses
- // the list passed in to it. We don't want callers subsequently mutating.
- Exception[] exceptionsCopy = new Exception[innerExceptionInfos.Count];
- for (int i = 0; i < exceptionsCopy.Length; i++)
- {
- var edi = innerExceptionInfos[i];
- if (edi != null) exceptionsCopy[i] = edi.SourceException;
- if (exceptionsCopy[i] == null)
- {
- throw new ArgumentException(SR.AggregateException_ctor_InnerExceptionNull);
- }
- }
- m_innerExceptions = new ReadOnlyCollection<Exception>(exceptionsCopy);
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="AggregateException"/> class with serialized data.
- /// </summary>
- /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds
- /// the serialized object data about the exception being thrown.</param>
- /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that
- /// contains contextual information about the source or destination. </param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> argument is null.</exception>
- /// <exception cref="T:System.Runtime.Serialization.SerializationException">The exception could not be deserialized correctly.</exception>
- protected AggregateException(SerializationInfo info, StreamingContext context) :
- base(info, context)
- {
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
- Exception[] innerExceptions = info.GetValue("InnerExceptions", typeof(Exception[])) as Exception[];
- if (innerExceptions == null)
- {
- throw new SerializationException(SR.AggregateException_DeserializationFailure);
- }
- m_innerExceptions = new ReadOnlyCollection<Exception>(innerExceptions);
- }
- /// <summary>
- /// Sets the <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with information about
- /// the exception.
- /// </summary>
- /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds
- /// the serialized object data about the exception being thrown.</param>
- /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that
- /// contains contextual information about the source or destination. </param>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> argument is null.</exception>
- public override void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- base.GetObjectData(info, context);
- Exception[] innerExceptions = new Exception[m_innerExceptions.Count];
- m_innerExceptions.CopyTo(innerExceptions, 0);
- info.AddValue("InnerExceptions", innerExceptions, typeof(Exception[]));
- }
- /// <summary>
- /// Returns the <see cref="System.AggregateException"/> that is the root cause of this exception.
- /// </summary>
- public override Exception GetBaseException()
- {
- // Returns the first inner AggregateException that contains more or less than one inner exception
- // Recursively traverse the inner exceptions as long as the inner exception of type AggregateException and has only one inner exception
- Exception back = this;
- AggregateException backAsAggregate = this;
- while (backAsAggregate != null && backAsAggregate.InnerExceptions.Count == 1)
- {
- back = back.InnerException;
- backAsAggregate = back as AggregateException;
- }
- return back;
- }
- /// <summary>
- /// Gets a read-only collection of the <see cref="T:System.Exception"/> instances that caused the
- /// current exception.
- /// </summary>
- public ReadOnlyCollection<Exception> InnerExceptions
- {
- get { return m_innerExceptions; }
- }
- /// <summary>
- /// Invokes a handler on each <see cref="T:System.Exception"/> contained by this <see
- /// cref="AggregateException"/>.
- /// </summary>
- /// <param name="predicate">The predicate to execute for each exception. The predicate accepts as an
- /// argument the <see cref="T:System.Exception"/> to be processed and returns a Boolean to indicate
- /// whether the exception was handled.</param>
- /// <remarks>
- /// Each invocation of the <paramref name="predicate"/> returns true or false to indicate whether the
- /// <see cref="T:System.Exception"/> was handled. After all invocations, if any exceptions went
- /// unhandled, all unhandled exceptions will be put into a new <see cref="AggregateException"/>
- /// which will be thrown. Otherwise, the <see cref="Handle"/> method simply returns. If any
- /// invocations of the <paramref name="predicate"/> throws an exception, it will halt the processing
- /// of any more exceptions and immediately propagate the thrown exception as-is.
- /// </remarks>
- /// <exception cref="AggregateException">An exception contained by this <see
- /// cref="AggregateException"/> was not handled.</exception>
- /// <exception cref="T:System.ArgumentNullException">The <paramref name="predicate"/> argument is
- /// null.</exception>
- public void Handle(Func<Exception, bool> predicate)
- {
- if (predicate == null)
- {
- throw new ArgumentNullException(nameof(predicate));
- }
- List<Exception> unhandledExceptions = null;
- for (int i = 0; i < m_innerExceptions.Count; i++)
- {
- // If the exception was not handled, lazily allocate a list of unhandled
- // exceptions (to be rethrown later) and add it.
- if (!predicate(m_innerExceptions[i]))
- {
- if (unhandledExceptions == null)
- {
- unhandledExceptions = new List<Exception>();
- }
- unhandledExceptions.Add(m_innerExceptions[i]);
- }
- }
- // If there are unhandled exceptions remaining, throw them.
- if (unhandledExceptions != null)
- {
- throw new AggregateException(Message, unhandledExceptions);
- }
- }
- /// <summary>
- /// Flattens the inner instances of <see cref="AggregateException"/> by expanding its contained <see cref="Exception"/> instances
- /// into a new <see cref="AggregateException"/>
- /// </summary>
- /// <returns>A new, flattened <see cref="AggregateException"/>.</returns>
- /// <remarks>
- /// If any inner exceptions are themselves instances of
- /// <see cref="AggregateException"/>, this method will recursively flatten all of them. The
- /// inner exceptions returned in the new <see cref="AggregateException"/>
- /// will be the union of all of the inner exceptions from exception tree rooted at the provided
- /// <see cref="AggregateException"/> instance.
- /// </remarks>
- public AggregateException Flatten()
- {
- // Initialize a collection to contain the flattened exceptions.
- List<Exception> flattenedExceptions = new List<Exception>();
- // Create a list to remember all aggregates to be flattened, this will be accessed like a FIFO queue
- List<AggregateException> exceptionsToFlatten = new List<AggregateException>();
- exceptionsToFlatten.Add(this);
- int nDequeueIndex = 0;
- // Continue removing and recursively flattening exceptions, until there are no more.
- while (exceptionsToFlatten.Count > nDequeueIndex)
- {
- // dequeue one from exceptionsToFlatten
- IList<Exception> currentInnerExceptions = exceptionsToFlatten[nDequeueIndex++].InnerExceptions;
- for (int i = 0; i < currentInnerExceptions.Count; i++)
- {
- Exception currentInnerException = currentInnerExceptions[i];
- if (currentInnerException == null)
- {
- continue;
- }
- // If this exception is an aggregate, keep it around for later. Otherwise,
- // simply add it to the list of flattened exceptions to be returned.
- if (currentInnerException is AggregateException currentInnerAsAggregate)
- {
- exceptionsToFlatten.Add(currentInnerAsAggregate);
- }
- else
- {
- flattenedExceptions.Add(currentInnerException);
- }
- }
- }
- return new AggregateException(Message, flattenedExceptions);
- }
- /// <summary>Gets a message that describes the exception.</summary>
- public override string Message
- {
- get
- {
- if (m_innerExceptions.Count == 0)
- {
- return base.Message;
- }
- StringBuilder sb = StringBuilderCache.Acquire();
- sb.Append(base.Message);
- sb.Append(' ');
- for (int i = 0; i < m_innerExceptions.Count; i++)
- {
- sb.Append('(');
- sb.Append(m_innerExceptions[i].Message);
- sb.Append(") ");
- }
- sb.Length -= 1;
- return StringBuilderCache.GetStringAndRelease(sb);
- }
- }
- /// <summary>
- /// Creates and returns a string representation of the current <see cref="AggregateException"/>.
- /// </summary>
- /// <returns>A string representation of the current exception.</returns>
- public override string ToString()
- {
- StringBuilder text = new StringBuilder();
- text.Append(base.ToString());
- for (int i = 0; i < m_innerExceptions.Count; i++)
- {
- text.AppendLine();
- text.Append("---> ");
- text.AppendFormat(CultureInfo.InvariantCulture, SR.AggregateException_InnerException, i);
- text.Append(m_innerExceptions[i].ToString());
- text.Append("<---");
- text.AppendLine();
- }
- return text.ToString();
- }
- /// <summary>
- /// This helper property is used by the DebuggerDisplay.
- ///
- /// Note that we don't want to remove this property and change the debugger display to {InnerExceptions.Count}
- /// because DebuggerDisplay should be a single property access or parameterless method call, so that the debugger
- /// can use a fast path without using the expression evaluator.
- ///
- /// See https://docs.microsoft.com/en-us/visualstudio/debugger/using-the-debuggerdisplay-attribute
- /// </summary>
- private int InnerExceptionCount
- {
- get
- {
- return InnerExceptions.Count;
- }
- }
- }
- }
|