/// <summary>
        /// Compares substrings of two specified strings using the specified comparison rules,
        /// and returns an integer that indicates their relative position in the sort order.
        /// </summary>
        /// <param name="strA">The first string to use in the comparison.</param>
        /// <param name="indexA">The zero-based starting character position of the substring within <paramref name="strA"/>.</param>
        /// <param name="lengthA">The number of characters constituting the substring from <paramref name="strA"/>.</param>
        /// <param name="strB">The second string to use in the comparison.</param>
        /// <param name="indexB">The zero-based starting character position of the substring within <paramref name="strB"/>.</param>
        /// <param name="lengthB">The number of characters constituting the substring from <paramref name="strB"/>.</param>
        /// <param name="comparisonType">One of the enumeration values that specifies the rules to use in the comparison.</param>
        /// <returns>
        /// A signed integer that indicates the lexical relationship between the two comparands.
        /// <list type="table">
        /// <listheader>
        /// <term>Value</term>
        /// <term>Condition</term>
        /// </listheader>
        /// <item>
        /// <term>Less than zero</term>
        /// <term>The substring in <paramref name="strA"/> is less than the substring in <paramref name="strB"/>.</term>
        /// </item>
        /// <item>
        /// <term>Zero</term>
        /// <term>The substrings are equal, or <paramref name="lengthA"/> and <paramref name="lengthB"/> are both zero.</term>
        /// </item>
        /// <item>
        /// <term>Greater than zero</term>
        /// <term>The substring in <paramref name="strA"/> is greater than the substring in <paramref name="strB"/>.</term>
        /// </item>
        /// </list>
        /// </returns>
        /// <remarks>
        /// <para>
        /// This method is similar to the <see cref="string.Compare(string, int, string, int, int, StringComparison)"/> method
        /// in the .NET Framework Class Library, but allows different lengths to be specified for the two substrings.
        /// It is implemented by calling the <see cref="CompareInfo.Compare(string, int, int, string, int, int, CompareOptions)"/> method
        /// on the appropriate <see cref="CompareInfo"/> instance with the appropriate <see cref="CompareOptions"/> value
        /// for each known value of <paramref name="comparisonType"/>.
        /// For performance, substring instantiation is avoided, working with the start indexes and lengths instead.
        /// </para>
        /// <para>
        /// The implementation of this method is adapted from the internal implementations for
        /// <see cref="string.Compare(string, int, string, int, int, StringComparison)"/>
        /// (<see href="https://referencesource.microsoft.com/#mscorlib/system/string.cs,1ae4d07b01230bb6">source</see>)
        /// and <see cref="string.IndexOf(string, int, int, StringComparison)"/>
        /// (<see href="https://referencesource.microsoft.com/#mscorlib/system/string.cs,ef82268cfee756fe">source</see>).
        /// </para>
        /// </remarks>
        public static int Compare(string strA, int indexA, int lengthA, string strB, int indexB, int lengthB, StringComparison comparisonType)
        {
            ArgumentValidate.EnumDefined(comparisonType, nameof(comparisonType));

            if (strA == null)
            {
                return(strB == null ? 0 : -1);
            }
            if (strB == null)
            {
                return(1);
            }

            ArgumentValidate.StringIndexLength(strA, nameof(strA), indexA, nameof(indexA), lengthA, nameof(lengthA));
            ArgumentValidate.StringIndexLength(strB, nameof(strB), indexB, nameof(indexB), lengthB, nameof(lengthB));

            if (lengthA == 0 && lengthB == 0)
            {
                return(0);
            }
            if (string.ReferenceEquals(strA, strB) && indexA == indexB && lengthA == lengthB)
            {
                return(0);
            }

            return(CompareInner(strA, indexA, lengthA, strB, indexB, lengthB, comparisonType));
        }
        /// <summary>
        /// Reports the zero-based index and length of the first occurrence of the specified substring in the source string.
        /// </summary>
        /// <param name="source">The source string in which to search.</param>
        /// <param name="substring">The substring to seek.</param>
        /// <param name="comparisonType">One of the enumeration values that specifies the rules for the search.</param>
        /// <param name="matchIndex">
        /// When this method returns, contains the zero-based starting character position of the match, if found;
        /// or -1 if no match is found.
        /// If <paramref name="substring"/> is the empty string (<c>""</c>), the value will be 0.
        /// </param>
        /// <param name="matchLength">
        /// When this method returns, contains the length (in characters) of the match, if found;
        /// or -1 if no match is found.
        /// If <paramref name="substring"/> is the empty string (<c>""</c>), the value will be 0.
        /// </param>
        /// <remarks>
        /// Refer to the remarks on the <see cref="Find(string, string, int, int, StringComparison, out int, out int)"/> overload.
        /// </remarks>
        public static void Find(this string source, string substring, StringComparison comparisonType, out int matchIndex, out int matchLength)
        {
            ArgumentValidate.NotNull(source, nameof(source));
            ArgumentValidate.NotNull(substring, nameof(substring));
            ArgumentValidate.EnumDefined(comparisonType, nameof(comparisonType));

            FindInner(source, substring, 0, source.Length, comparisonType, out matchIndex, out matchLength);
        }
        /// <summary>
        /// Returns a value indicating whether the specified substring occurs within the source string,
        /// using the specified string comparison for the search.
        /// </summary>
        /// <param name="source">The source string in which to search.</param>
        /// <param name="value">The string to seek.</param>
        /// <param name="comparisonType">One of the enumeration values that specifies the rules for the search.</param>
        /// <returns>
        /// <see langword="true"/> if the <paramref name="value"/> parameter occurs within the <paramref name="source"/> string,
        /// or if <paramref name="value"/> is the empty string (<c>""</c>);
        /// otherwise, <see langword="false"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="value"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="comparisonType"/> is not a valid <see cref="StringComparison"/> value.</exception>
        /// <remarks>
        /// The built-in <see cref="string.Contains(string)"/> method performs an ordinal (case-sensitive and culture-insensitive) comparison.
        /// This extension method allows the comparison type to be specified.
        /// </remarks>
        public static bool Contains(this string source, string value, StringComparison comparisonType)
        {
            ArgumentValidate.NotNull(source, nameof(source));
            ArgumentValidate.NotNull(value, nameof(value));
            ArgumentValidate.EnumDefined(comparisonType, nameof(comparisonType));

            return(source.IndexOf(value, comparisonType) >= 0);
        }
        /// <summary>
        /// Reports the zero-based index and length of the first occurrence of the specified substring in the source string.
        /// </summary>
        /// <param name="source">The source string in which to search.</param>
        /// <param name="searchValue">The substring to seek.</param>
        /// <param name="comparisonType">One of the enumeration values that specifies the rules for the search.</param>
        /// <param name="matchIndex">
        /// When this method returns, contains the zero-based starting character position of the match, if found;
        /// or -1 if no match is found.
        /// If <paramref name="searchValue"/> is the empty string (<c>""</c>), the value will be 0.
        /// </param>
        /// <param name="matchLength">
        /// When this method returns, contains the length (in characters) of the match, if found;
        /// or -1 if no match is found.
        /// If <paramref name="searchValue"/> is the empty string (<c>""</c>), the value will be 0.
        /// </param>
        /// <returns>
        /// <see langword="true"/> if a match for <paramref name="searchValue"/> is found in the source string;
        /// otherwise, <see langword="false"/>.
        /// </returns>
        /// <remarks>
        /// Refer to the remarks on the
        /// <see cref="Find(string, string, int, int, StringComparison, out int, out int)"/> overload.
        /// </remarks>
        public static bool Find(this string source, string searchValue, StringComparison comparisonType, out int matchIndex, out int matchLength)
        {
            ArgumentValidate.NotNull(source, nameof(source));
            ArgumentValidate.NotNull(searchValue, nameof(searchValue));
            ArgumentValidate.EnumDefined(comparisonType, nameof(comparisonType));

            return(FindInner(source, searchValue, 0, source.Length, comparisonType, out matchIndex, out matchLength));
        }
        /// <summary>
        /// Executes the specified asynchronous function delegate using the disposable resource,
        /// then disposes of the said resource by calling its <see cref="IDisposable.Dispose()"/> method.
        /// </summary>
        /// <typeparam name="TDisposable">The type of the disposable resource to use.</typeparam>
        /// <typeparam name="TResult">The type of the return value of the asynchronous function delegate.</typeparam>
        /// <param name="disposable">The disposable resource to use.</param>
        /// <param name="strategy">
        /// The strategy for propagating or swallowing exceptions thrown by the <see cref="IDisposable.Dispose"/> method.
        /// </param>
        /// <param name="asyncFunc">The asynchronous function delegate to execute using the disposable resource.</param>
        /// <returns>
        /// A task that represents the asynchronous operation.
        /// The task result contains the return value of the asynchronous function delegate.
        /// </returns>
        /// <exception cref="ArgumentNullException"><paramref name="disposable"/> or <paramref name="asyncFunc"/> is <see langword="null"/>.</exception>
        /// <remarks>
        /// Refer to the remarks on the <see cref="Using{TDisposable}(TDisposable, DisposeExceptionStrategy, Action{TDisposable})"/> overload.
        /// </remarks>
        public static Task <TResult> UsingAsync <TDisposable, TResult>(this TDisposable disposable, DisposeExceptionStrategy strategy, Func <TDisposable, Task <TResult> > asyncFunc)
            where TDisposable : IDisposable
        {
            ArgumentValidate.NotNull(disposable, nameof(disposable));
            ArgumentValidate.NotNull(asyncFunc, nameof(asyncFunc));
            ArgumentValidate.EnumDefined(strategy, nameof(strategy));

            return(disposable.UsingAsyncInner(strategy, asyncFunc));
        }
        /// <summary>
        /// Executes the specified asynchronous action delegate using the disposable resource,
        /// then disposes of the said resource by calling its <see cref="IDisposable.Dispose()"/> method.
        /// </summary>
        /// <typeparam name="TDisposable">The type of the disposable resource to use.</typeparam>
        /// <param name="disposable">The disposable resource to use.</param>
        /// <param name="strategy">
        /// The strategy for propagating or swallowing exceptions thrown by the <see cref="IDisposable.Dispose"/> method.
        /// </param>
        /// <param name="asyncAction">The asynchronous action delegate to execute using the disposable resource.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="disposable"/> or <paramref name="asyncAction"/> is <see langword="null"/>.</exception>
        /// <remarks>
        /// Refer to the remarks on the <see cref="Using{TDisposable}(TDisposable, DisposeExceptionStrategy, Action{TDisposable})"/> overload.
        /// </remarks>
        public static Task UsingAsync <TDisposable>(this TDisposable disposable, DisposeExceptionStrategy strategy, Func <TDisposable, Task> asyncAction)
            where TDisposable : IDisposable
        {
            ArgumentValidate.NotNull(disposable, nameof(disposable));
            ArgumentValidate.NotNull(asyncAction, nameof(asyncAction));
            ArgumentValidate.EnumDefined(strategy, nameof(strategy));

            var asyncFunc = asyncAction.ReturnAsync(true);

            return(disposable.UsingAsync(strategy, asyncFunc));
        }
        /// <summary>
        /// Executes the specified action delegate using the disposable resource,
        /// then disposes of the said resource by calling its <see cref="IDisposable.Dispose()"/> method.
        /// </summary>
        /// <typeparam name="TDisposable">The type of the disposable resource to use.</typeparam>
        /// <param name="disposable">The disposable resource to use.</param>
        /// <param name="strategy">
        /// The strategy for propagating or swallowing exceptions thrown by the <see cref="IDisposable.Dispose"/> method.
        /// </param>
        /// <param name="action">The action delegate to execute using the disposable resource.</param>
        /// <exception cref="ArgumentNullException"><paramref name="disposable"/> or <paramref name="action"/> is <see langword="null"/>.</exception>
        /// <remarks>
        /// <para>
        /// This extension method enhances the functionality of the <see langword="using"/> keyword by providing alternative strategies
        /// for propagating or swallowing exceptions thrown by the <see cref="IDisposable.Dispose"/> method,
        /// in conjunction with exceptions thrown by the main logic of the <paramref name="action"/> delegate.
        /// </para>
        /// <para>
        /// The standard behavior of the <see langword="using"/> keyword causes any exceptions from the main logic
        /// to be hidden and lost if an exception is also thrown from <see cref="IDisposable.Dispose"/>.
        /// According to Sections 8.9.5 and 8.10 of the C# Language Specification (version 5.0):
        /// </para>
        /// <blockquote>
        /// If the <see langword="finally"/> block throws another exception, processing of the current exception
        /// [that was thrown from the <see langword="try"/> block or a <see langword="catch"/> block] is terminated.
        /// </blockquote>
        /// <blockquote>
        /// If an exception is thrown during execution of a <see langword="finally"/> block,
        /// and is not caught within the same <see langword="finally"/> block,
        /// the exception is propagated to the next enclosing <see langword="try"/> statement.
        /// If another exception was in the process of being propagated, that exception is lost.
        /// </blockquote>
        /// <para>
        /// MSDN cautions against the throwing of exceptions from the <see cref="IDisposable.Dispose"/> method.
        /// </para>
        /// <blockquote>
        /// X AVOID throwing an exception from within <see cref="IDisposable.Dispose"/> except under critical situations
        /// where the containing process has been corrupted (leaks, inconsistent shared state, etc.).
        /// Users expect that a call to <see cref="IDisposable.Dispose"/> will not raise an exception.
        /// </blockquote>
        /// <para>
        /// However, this guideline is broken repeatedly throughout the .NET Framework, including in the <see cref="FileStream"/> class,
        /// which can throw <see cref="IOException"/> whilst flushing its buffered data to the underlying device,
        /// and notoriously from the <see href="https://docs.microsoft.com/en-us/dotnet/api/system.servicemodel.clientbase-1">ClientBase&lt;TChannel&gt;</see> base class for WCF clients,
        /// which recommends ditching the <see langword="using"/> keyword to avoid this issue
        /// in <see href="https://docs.microsoft.com/en-us/dotnet/framework/wcf/samples/use-close-abort-release-wcf-client-resources">Close and Abort release resources safely when network connections have dropped</see>.
        /// </para>
        /// <para>
        /// The problematic implications of such exception swallowing are discussed
        /// in this <see href="https://stackoverflow.com/q/577607/1149773">Stack Overflow question</see>:
        /// </para>
        /// <blockquote>
        /// <list type="number">
        /// <item>A first exception is thrown</item>
        /// <item>A finally block is executed as a result of the first exception</item>
        /// <item>The finally block calls a Dispose() method</item>
        /// <item>The Dispose() method throws a second exception</item>
        /// </list>
        /// […] You lose information because .NET unceremoneously replaces the first exception with the second one.
        /// A catch block somewhere up the call stack will therefore never see the first exception.
        /// However, one is usually more interested in the first exception because that normally gives better clues
        /// as to why things started to go wrong.
        /// </blockquote>
        /// <para>
        /// This extension method aims to provide a conclusive resolution to this issue by implementing the various plausible strategies,
        /// including the standard behavior, and leaving it up to the caller to decide which to use.
        /// </para>
        /// <list type="bullet">
        /// <listheader>References</listheader>
        /// <item><see href="https://stackoverflow.com/q/577607/1149773">Should you implement IDisposable.Dispose() so that it never throws?</see>, <i>Stack Overflow</i></item>
        /// <item><see href="https://stackoverflow.com/q/35602760/1149773">Delegate-parameterized method vs IDisposable implementation"</see>, <i>Stack Overflow</i></item>
        /// <item><see href="https://stackoverflow.com/q/1654487/1149773">Swallowing exception thrown in catch/finally block"</see>, <i>Stack Overflow</i></item>
        /// <item><see href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose">Implementing a Dispose Method</see>, <i>MSDN Library</i></item>
        /// </list>
        /// </remarks>
        /// <example>
        /// <code>
        /// new FileStream(Path.GetTempFileName(), FileMode.Create)
        ///     .Using(strategy: DisposeExceptionStrategy.Subjugate, action: fileStream =>
        ///     {
        ///         // Access fileStream here
        ///         fileStream.WriteByte(42);
        ///         throw new InvalidOperationException();
        ///     });
        ///     // Any Dispose() exceptions will be swallowed due to the above InvalidOperationException
        /// </code>
        /// </example>
        public static void Using <TDisposable>(this TDisposable disposable, DisposeExceptionStrategy strategy, Action <TDisposable> action)
            where TDisposable : IDisposable
        {
            ArgumentValidate.NotNull(disposable, nameof(disposable));
            ArgumentValidate.NotNull(action, nameof(action));
            ArgumentValidate.EnumDefined(strategy, nameof(strategy));

            var func = action.Return(true);

            disposable.Using(strategy, func);
        }
        /// <summary>
        /// Executes the specified function delegate using the disposable resource,
        /// then disposes of the said resource by calling its <see cref="IDisposable.Dispose()"/> method.
        /// </summary>
        /// <typeparam name="TDisposable">The type of the disposable resource to use.</typeparam>
        /// <typeparam name="TResult">The type of the return value of the function delegate.</typeparam>
        /// <param name="disposable">The disposable resource to use.</param>
        /// <param name="strategy">
        /// The strategy for propagating or swallowing exceptions thrown by the <see cref="IDisposable.Dispose"/> method.
        /// </param>
        /// <param name="func">The function delegate to execute using the disposable resource.</param>
        /// <returns>The return value of the function delegate.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="disposable"/> or <paramref name="func"/> is <see langword="null"/>.</exception>
        /// <remarks>
        /// Refer to the remarks on the <see cref="Using{TDisposable}(TDisposable, DisposeExceptionStrategy, Action{TDisposable})"/> overload.
        /// </remarks>
        public static TResult Using <TDisposable, TResult>(this TDisposable disposable, DisposeExceptionStrategy strategy, Func <TDisposable, TResult> func)
            where TDisposable : IDisposable
        {
            ArgumentValidate.NotNull(disposable, nameof(disposable));
            ArgumentValidate.NotNull(func, nameof(func));
            ArgumentValidate.EnumDefined(strategy, nameof(strategy));

            var asyncFunc     = func.WrapAsync();
            var completedTask = disposable.UsingAsync(strategy, asyncFunc);

            return(completedTask.GetResult());   // task is always completed; returns immediately
        }
Esempio n. 9
0
        public void EnumDefined()
        {
            foreach (var comparison in EnumUtility.GetValues <StringComparison>())
            {
                ArgumentValidate.EnumDefined(comparison, nameof(comparison));
            }

            var invalidComparison = (StringComparison)int.MaxValue;
            var exception         = ExceptionAssert.Throws <ArgumentException>(() =>
                                                                               ArgumentValidate.EnumDefined(invalidComparison, nameof(invalidComparison)));

            Assert.AreEqual(nameof(invalidComparison), exception.ParamName);
        }