diff options
author | David Wrighton <davidwr@microsoft.com> | 2017-09-13 14:50:39 -0700 |
---|---|---|
committer | David Wrighton <davidwr@microsoft.com> | 2017-09-13 14:50:39 -0700 |
commit | d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24 (patch) | |
tree | 6c21ac239ae268096f20d98a8db16a4b80394fd9 /src/mscorlib/shared/System/AggregateException.cs | |
parent | 96fa98525e0d64459148228cde5211c475b0c25c (diff) | |
parent | e866d072042f4ad9e0811aa36e338dac781c09a5 (diff) | |
download | coreclr-d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24.tar.gz coreclr-d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24.tar.bz2 coreclr-d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24.zip |
Merge branch 'master' into update_from_master
Diffstat (limited to 'src/mscorlib/shared/System/AggregateException.cs')
-rw-r--r-- | src/mscorlib/shared/System/AggregateException.cs | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/AggregateException.cs b/src/mscorlib/shared/System/AggregateException.cs new file mode 100644 index 0000000000..36b9494980 --- /dev/null +++ b/src/mscorlib/shared/System/AggregateException.cs @@ -0,0 +1,484 @@ +// 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; + } + + AggregateException currentInnerAsAggregate = currentInnerException as AggregateException; + + // 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 (currentInnerAsAggregate != null) + { + 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.Append(Environment.NewLine); + text.Append("---> "); + text.Append(string.Format(CultureInfo.InvariantCulture, SR.AggregateException_InnerException, i)); + text.Append(m_innerExceptions[i].ToString()); + text.Append("<---"); + text.Append(Environment.NewLine); + } + + 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 http://msdn.microsoft.com/en-us/library/x810d419.aspx + /// </summary> + private int InnerExceptionCount + { + get + { + return InnerExceptions.Count; + } + } + } +} |