Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactors Guard classes. #250

Merged
merged 1 commit into from
Jun 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/CatLib.Core.Tests/Support/TestsGuard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* This file is part of the CatLib package.
*
* (c) CatLib <support@catlib.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Document: https://catlib.io/
*/

#pragma warning disable CA1031

using CatLib.Support;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace CatLib.Tests.Support
{
[TestClass]
public class TestsGuard
{
[TestMethod]
public void TestRequires()
{
var innerException = new Exception("inner exception");

try
{
Guard.Requires<Exception>(false, "foo", innerException);
Assert.Fail();
}
catch (Exception ex)
{
Assert.AreEqual("foo", ex.Message);
Assert.AreEqual("inner exception", ex.InnerException.Message);
}
}

[TestMethod]
public void TestExtend()
{
var innerException = new Exception("inner exception");

Guard.Extend<ArgumentNullException>((messgae, inner, state) =>
{
return new ArgumentNullException("foo", inner);
});

try
{
Guard.Requires<ArgumentNullException>(false, null, innerException);
Assert.Fail();
}
catch (Exception ex)
{
Assert.AreEqual("foo", ex.Message);
Assert.AreEqual("inner exception", ex.InnerException.Message);
}
}

[TestMethod]
public void TestRequireNotBaseException()
{
var innerException = new Exception("inner exception");

try
{
Guard.Requires<ArgumentNullException>(false, "foo", innerException);
Assert.Fail();
}
catch (Exception ex)
{
Assert.AreEqual("foo", ex.Message);
Assert.AreEqual("inner exception", ex.InnerException.Message);
}
}
}
}
2 changes: 1 addition & 1 deletion src/CatLib.Core/Container/BindData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ protected override void ReleaseBind()

private void AddClosure(Action<IBindData, object> closure, ref List<Action<IBindData, object>> list)
{
Guard.NotNull(closure, nameof(closure));
Guard.Requires<ArgumentNullException>(closure != null);

lock (Locker)
{
Expand Down
20 changes: 12 additions & 8 deletions src/CatLib.Core/Container/Container.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,10 @@ public object this[string service]
public void Tag(string tag, params string[] service)
{
Guard.NotEmptyOrNull(tag, nameof(tag));
Guard.NotNull(service, nameof(service));
Guard.CountGreaterZero(service, nameof(service));
Guard.ElementNotEmptyOrNull(service, nameof(service));
Guard.Requires<ArgumentNullException>(service != null);
Guard.Requires<ArgumentNullException>(service.Length > 0);
Guard.Requires<ArgumentNullException>(
!Array.Exists(service, (s) => string.IsNullOrEmpty(s)));

lock (syncRoot)
{
Expand Down Expand Up @@ -377,7 +378,8 @@ public bool BindIf(string service, Type concrete, bool isStatic, out IBindData b
/// <inheritdoc />
public IBindData Bind(string service, Type concrete, bool isStatic)
{
Guard.NotNull(concrete, nameof(concrete));
Guard.Requires<ArgumentNullException>(concrete != null);

if (IsUnableType(concrete))
{
throw new LogicException($"Bind type [{concrete}] can not built");
Expand All @@ -391,7 +393,7 @@ public IBindData Bind(string service, Type concrete, bool isStatic)
public IBindData Bind(string service, Func<IContainer, object[], object> concrete, bool isStatic)
{
Guard.NotEmptyOrNull(service, nameof(service));
Guard.NotNull(concrete, nameof(concrete));
Guard.Requires<ArgumentNullException>(concrete != null);
GuardServiceName(service);

service = FormatService(service);
Expand Down Expand Up @@ -664,7 +666,8 @@ public bool Release(object mixed)
/// <inheritdoc />
public IContainer OnFindType(Func<string, Type> func, int priority = int.MaxValue)
{
Guard.NotNull(func, nameof(func));
Guard.Requires<ArgumentNullException>(func != null);

lock (syncRoot)
{
GuardFlushing();
Expand Down Expand Up @@ -698,7 +701,8 @@ public IContainer OnAfterResolving(Action<IBindData, object> closure)
/// <inheritdoc />
public IContainer OnRebound(string service, Action<object> callback)
{
Guard.NotNull(callback, nameof(callback));
Guard.Requires<ArgumentNullException>(callback != null);

lock (syncRoot)
{
GuardFlushing();
Expand Down Expand Up @@ -1902,7 +1906,7 @@ private Func<ParameterInfo, object> MakeParamsMatcher(IParams[] tables)
/// <param name="list">The specified list.</param>
private void AddClosure(Action<IBindData, object> closure, List<Action<IBindData, object>> list)
{
Guard.NotNull(closure, nameof(closure));
Guard.Requires<ArgumentNullException>(closure != null);

lock (syncRoot)
{
Expand Down
120 changes: 88 additions & 32 deletions src/CatLib.Core/Support/Guard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

using System;
using System.Collections.Generic;
using System.Reflection;

namespace CatLib.Support
{
Expand All @@ -20,6 +21,7 @@ namespace CatLib.Support
public sealed class Guard
{
private static Guard that;
private static IDictionary<Type, Func<string, Exception, object, Exception>> exceptionFactory;

/// <summary>
/// Gets the singleton instance of the Guard functionality.
Expand All @@ -43,75 +45,129 @@ public static Guard That
/// </summary>
/// <typeparam name="TException">Exception triggered when validation fails.</typeparam>
/// <param name="condition">The condition of the contract.</param>
/// <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, or a null reference.</param>
/// <param name="state">State will be passed to the registered exception build factory.</param>
[System.Diagnostics.DebuggerNonUserCode]
public static void Requires<TException>(bool condition)
public static void Requires<TException>(bool condition, string message = null, Exception innerException = null, object state = null)
where TException : Exception, new()
{
Requires(typeof(TException), condition, message, innerException, state);
}

/// <summary>
/// Verifies a condition and throws an exception if the condition of the contract fails.
/// </summary>
/// <param name="exception">Exception triggered when validation fails.</param>
/// <param name="condition">The condition of the contract.</param>
/// <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, or a null reference.</param>
/// <param name="state">State will be passed to the registered exception build factory.</param>
[System.Diagnostics.DebuggerNonUserCode]
public static void Requires(Type exception, bool condition, string message = null, Exception innerException = null, object state = null)
{
if (condition)
{
return;
}

throw new TException();
throw CreateExceptionInstance(exception, message, innerException, state);
}

/// <summary>
/// Verifies is not empty or null.
/// </summary>
/// <param name="argumentValue">The parameter value.</param>
/// <param name="argumentName">The parameter name.</param>
/// <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, or a null reference.</param>
[System.Diagnostics.DebuggerNonUserCode]
public static void NotEmptyOrNull(string argumentValue, string argumentName)
public static void NotEmptyOrNull(string argumentValue, string argumentName = null, string message = null, Exception innerException = null)
{
if (string.IsNullOrEmpty(argumentValue))
if (!string.IsNullOrEmpty(argumentValue))
{
return;
}

var exception = new ArgumentNullException(argumentName, message);

if (innerException != null)
{
throw new ArgumentNullException(argumentName);
SetField(exception, "_innerException", innerException);
}

throw exception;
}

/// <summary>
/// Verifies the length is greater than 0.
/// Extend an exception generation factory.
/// </summary>
/// <typeparam name="T">The type of parameter.</typeparam>
/// <param name="argumentValue">The parameter value.</param>
/// <param name="argumentName">The parameter name.</param>
/// <typeparam name="T">The type of exception.</typeparam>
/// <param name="factory">The exception factory.</param>
[System.Diagnostics.DebuggerNonUserCode]
public static void CountGreaterZero<T>(IList<T> argumentValue, string argumentName)
public static void Extend<T>(Func<string, Exception, object, Exception> factory)
{
if (argumentValue.Count <= 0)
{
throw new ArgumentNullException(argumentName);
}
Extend(typeof(T), factory);
}

/// <summary>
/// Verifies the element not empty or null.
/// Extend an exception generation factory.
/// </summary>
/// <param name="argumentValue">The parameter value.</param>
/// <param name="argumentName">The parameter name.</param>
/// <param name="exception">The type of exception.</param>
/// <param name="factory">The exception factory.</param>
[System.Diagnostics.DebuggerNonUserCode]
public static void ElementNotEmptyOrNull(IList<string> argumentValue, string argumentName)
public static void Extend(Type exception, Func<string, Exception, object, Exception> factory)
{
VerfiyExceptionFactory();
exceptionFactory[exception] = factory;
}

private static Exception CreateExceptionInstance(Type exceptionType, string message, Exception innerException, object state)
{
foreach (var val in argumentValue)
if (!typeof(Exception).IsAssignableFrom(exceptionType))
{
if (string.IsNullOrEmpty(val))
{
throw new ArgumentNullException(argumentName, $"Argument element can not be {nameof(string.Empty)} or null.");
}
throw new ArgumentException(
$"Type: {exceptionType} must be inherited from: {typeof(Exception)}.",
nameof(exceptionType));
}

VerfiyExceptionFactory();

if (exceptionFactory.TryGetValue(exceptionType, out Func<string, Exception, object, Exception> factory))
{
return factory(message, innerException, state);
}

var exception = Activator.CreateInstance(exceptionType);
if (!string.IsNullOrEmpty(message))
{
SetField(exception, "_message", message);
}

if (innerException != null)
{
SetField(exception, "_innerException", innerException);
}

return (Exception)exception;
}

/// <summary>
/// Verifies the parameter not null.
/// </summary>
/// <param name="argumentValue">The parameter value.</param>
/// <param name="argumentName">The parameter name.</param>
[System.Diagnostics.DebuggerNonUserCode]
public static void NotNull(object argumentValue, string argumentName)
private static void VerfiyExceptionFactory()
{
if (argumentValue == null)
if (exceptionFactory == null)
{
exceptionFactory = new Dictionary<Type, Func<string, Exception, object, Exception>>();
}
}

private static void SetField(object obj, string field, object value)
{
var flag = BindingFlags.Instance | BindingFlags.NonPublic;
var fieldInfo = obj.GetType().GetField(field, flag);

if (fieldInfo != null)
{
throw new ArgumentNullException(argumentName);
fieldInfo.SetValue(obj, value);
}
}
}
Expand Down