From df331dc26db6d26cdd674aa8b2a0fbf71c50ac71 Mon Sep 17 00:00:00 2001 From: Simon G Date: Wed, 7 Sep 2022 10:28:33 +0200 Subject: [PATCH] - remove IocIgnoreConstructorAttribute - allow registration of custom ignoreConstructor attribute --- .../IocIgnoreConstructorAttribute.cs | 16 ---- ...alidIgnoreConstructorAttributeException.cs | 21 +++++ .../Interfaces/IIocContainer.cs | 8 ++ LightweightIocContainer/IocContainer.cs | 30 +++++-- .../LightweightIocContainer.xml | 36 ++++++-- .../IocValidatorTest.cs | 12 +-- .../IgnoreConstructorAttributeTest.cs | 82 +++++++++++++++++++ 7 files changed, 163 insertions(+), 42 deletions(-) delete mode 100644 LightweightIocContainer/Annotations/IocIgnoreConstructorAttribute.cs create mode 100644 LightweightIocContainer/Exceptions/InvalidIgnoreConstructorAttributeException.cs create mode 100644 Test.LightweightIocContainer/IgnoreConstructorAttributeTest.cs diff --git a/LightweightIocContainer/Annotations/IocIgnoreConstructorAttribute.cs b/LightweightIocContainer/Annotations/IocIgnoreConstructorAttribute.cs deleted file mode 100644 index 9470dfd..0000000 --- a/LightweightIocContainer/Annotations/IocIgnoreConstructorAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Author: Gockner, Simon -// Created: 2022-09-01 -// Copyright(c) 2022 SimonG. All Rights Reserved. - -using LightweightIocContainer.Interfaces; - -namespace LightweightIocContainer.Annotations; - -/// -/// If a constructor is annotated with this attribute it will be ignored by the -/// -[AttributeUsage(AttributeTargets.Constructor)] -public class IocIgnoreConstructorAttribute : Attribute -{ - -} \ No newline at end of file diff --git a/LightweightIocContainer/Exceptions/InvalidIgnoreConstructorAttributeException.cs b/LightweightIocContainer/Exceptions/InvalidIgnoreConstructorAttributeException.cs new file mode 100644 index 0000000..8b46f7a --- /dev/null +++ b/LightweightIocContainer/Exceptions/InvalidIgnoreConstructorAttributeException.cs @@ -0,0 +1,21 @@ +// Author: Gockner, Simon +// Created: 2022-09-06 +// Copyright(c) 2022 SimonG. All Rights Reserved. + +namespace LightweightIocContainer.Exceptions; + +/// +/// The passed can't be used on a constructor +/// +/// The used +public class InvalidIgnoreConstructorAttributeException : IocContainerException +{ + /// + /// The passed can't be used on a constructor + /// + public InvalidIgnoreConstructorAttributeException() + : base($"The passed Attribute ({typeof(T)}) can't be used on a constructor.") + { + + } +} \ No newline at end of file diff --git a/LightweightIocContainer/Interfaces/IIocContainer.cs b/LightweightIocContainer/Interfaces/IIocContainer.cs index 4b0ec0d..dffad84 100644 --- a/LightweightIocContainer/Interfaces/IIocContainer.cs +++ b/LightweightIocContainer/Interfaces/IIocContainer.cs @@ -2,6 +2,7 @@ // Created: 2019-05-20 // Copyright(c) 2019 SimonG. All Rights Reserved. +using LightweightIocContainer.Exceptions; using LightweightIocContainer.Interfaces.Installers; using LightweightIocContainer.Interfaces.Registrations; @@ -37,4 +38,11 @@ public interface IIocContainer : IDisposable /// The given /// True if the given is registered with this , false if not bool IsTypeRegistered(); + + /// + /// Register a custom that can annotate a constructor to be ignored + /// + /// The custom + /// The passed can't be used on a constructor + void RegisterIgnoreConstructorAttribute() where T : Attribute; } \ No newline at end of file diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index 17155f9..0a82dce 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -4,7 +4,6 @@ using System.Reflection; using System.Runtime.CompilerServices; -using LightweightIocContainer.Annotations; using LightweightIocContainer.Exceptions; using LightweightIocContainer.Interfaces; using LightweightIocContainer.Interfaces.Factories; @@ -26,17 +25,20 @@ public class IocContainer : IIocContainer, IIocResolver private readonly List<(Type type, object? instance)> _singletons = new(); private readonly List<(Type type, Type scope, ConditionalWeakTable instances)> _multitons = new(); + private readonly List _ignoreConstructorAttributes; + /// /// The main container that carries all the s and can resolve all the types you'll ever want /// public IocContainer() { _registrationFactory = new RegistrationFactory(this); + _ignoreConstructorAttributes = new List(); Registrations = new List(); } internal List Registrations { get; } - + /// /// Install the given installers for the current /// @@ -197,7 +199,7 @@ public class IocContainer : IIocContainer, IIocResolver /// The registered /// The given arguments /// The current resolve stack - /// + /// True if resolve is called from factory, false (default) if not /// An instance of the given registered , an if parameters need to be resolved or an if a factory method is used to create an instance /// The given is not registered /// A direct resolve with a registered factory is not allowed @@ -259,11 +261,11 @@ public class IocContainer : IIocContainer, IIocResolver /// Resolve the given object instance without generic arguments /// /// The of the returned instance - /// + /// The given resolved object /// An instance of the given resolved object /// Resolve returned wrong type - private object? ResolveInstanceNonGeneric(Type type, object resolveObject) => - GenericMethodCaller.CallPrivate(this, nameof(ResolveInstance), type, resolveObject); + private object? ResolveInstanceNonGeneric(Type type, object resolvedObject) => + GenericMethodCaller.CallPrivate(this, nameof(ResolveInstance), type, resolvedObject); /// /// Creates an instance of a given @@ -581,7 +583,7 @@ public class IocContainer : IIocContainer, IIocResolver private List TryGetSortedConstructors(Type type) { List sortedConstructors = type.GetConstructors() - .Where(c => c.GetCustomAttribute() == null) + .Where(c => !c.GetCustomAttributes().Any(a => _ignoreConstructorAttributes.Contains(a.GetType()))) .OrderByDescending(c => c.GetParameters().Length) .ToList(); @@ -651,6 +653,20 @@ public class IocContainer : IIocContainer, IIocResolver /// True if the given is registered with this , false if not public bool IsTypeRegistered() => FindRegistration() != null; + /// + /// Register a custom that can annotate a constructor to be ignored + /// + /// The custom + /// The passed can't be used on a constructor + public void RegisterIgnoreConstructorAttribute() where T : Attribute + { + AttributeUsageAttribute? attributeUsage = typeof(T).GetCustomAttribute(); + if (attributeUsage == null || !attributeUsage.ValidOn.HasFlag(AttributeTargets.Constructor)) + throw new InvalidIgnoreConstructorAttributeException(); + + _ignoreConstructorAttributes.Add(typeof(T)); + } + /// /// The method /// diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index a69e5b6..502c9f8 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -13,11 +13,6 @@ The given to convert An converted from the given - - - If a constructor is annotated with this attribute it will be ignored by the - - Helper for dynamic instance creation @@ -214,6 +209,17 @@ The exception message + + + The passed can't be used on a constructor + + The used + + + + The passed can't be used on a constructor + + The registration is not valid @@ -526,6 +532,13 @@ The given True if the given is registered with this , false if not + + + Register a custom that can annotate a constructor to be ignored + + The custom + The passed can't be used on a constructor + Provides methods @@ -1011,7 +1024,7 @@ The registered The given arguments The current resolve stack - + True if resolve is called from factory, false (default) if not An instance of the given registered , an if parameters need to be resolved or an if a factory method is used to create an instance The given is not registered A direct resolve with a registered factory is not allowed @@ -1041,7 +1054,7 @@ Resolve the given object instance without generic arguments The of the returned instance - + The given resolved object An instance of the given resolved object Resolve returned wrong type @@ -1186,6 +1199,13 @@ The given True if the given is registered with this , false if not + + + Register a custom that can annotate a constructor to be ignored + + The custom + The passed can't be used on a constructor + The method @@ -1528,7 +1548,7 @@ Validate that no registration that isn't derived from has - + Can't register a type as Lifestyle.Multiton without a scope (Registration is not of type IMultitonRegistration) diff --git a/Test.LightweightIocContainer.Validation/IocValidatorTest.cs b/Test.LightweightIocContainer.Validation/IocValidatorTest.cs index aa3363b..3a7c091 100644 --- a/Test.LightweightIocContainer.Validation/IocValidatorTest.cs +++ b/Test.LightweightIocContainer.Validation/IocValidatorTest.cs @@ -4,7 +4,6 @@ using JetBrains.Annotations; using LightweightIocContainer; -using LightweightIocContainer.Annotations; using LightweightIocContainer.Exceptions; using LightweightIocContainer.Interfaces.Installers; using LightweightIocContainer.Interfaces.Registrations; @@ -33,15 +32,6 @@ public class IocValidatorTest public Test(IParameter parameter) => parameter.Method(); } - [UsedImplicitly] - private class TestViewModelIgnoreDesignTimeCtor : ITest - { - public TestViewModelIgnoreDesignTimeCtor(IParameter parameter) => parameter.Method(); - - [IocIgnoreConstructor] - public TestViewModelIgnoreDesignTimeCtor() => throw new Exception(); - } - [UsedImplicitly] private class TestViewModelDontIgnoreDesignTimeCtor : ITest { @@ -282,4 +272,4 @@ public class IocValidatorTest else Assert.Fail($"Exception is no {nameof(NoMatchingConstructorFoundException)}, actual type: {exception?.GetType()}"); } -} +} \ No newline at end of file diff --git a/Test.LightweightIocContainer/IgnoreConstructorAttributeTest.cs b/Test.LightweightIocContainer/IgnoreConstructorAttributeTest.cs new file mode 100644 index 0000000..22e9570 --- /dev/null +++ b/Test.LightweightIocContainer/IgnoreConstructorAttributeTest.cs @@ -0,0 +1,82 @@ +// Author: Gockner, Simon +// Created: 2022-09-07 +// Copyright(c) 2022 SimonG. All Rights Reserved. + +using JetBrains.Annotations; +using LightweightIocContainer; +using LightweightIocContainer.Exceptions; +using NUnit.Framework; + +namespace Test.LightweightIocContainer; + +public class IgnoreConstructorAttributeTest +{ + [AttributeUsage(AttributeTargets.Constructor)] + private class IgnoreAttribute : Attribute + { + + } + + [AttributeUsage(AttributeTargets.Class)] + private class InvalidIgnoreAttribute : Attribute + { + + } + + [UsedImplicitly] + public interface ITest + { + + } + + [UsedImplicitly] + private class TestIgnoreCtor : ITest + { + [UsedImplicitly] + private readonly string _name; + public TestIgnoreCtor(string name) => _name = name; + + [Ignore] + public TestIgnoreCtor() => throw new Exception(); + } + + [UsedImplicitly] + private class TestOnlyIgnoredCtor : ITest + { + [Ignore] + public TestOnlyIgnoredCtor() => throw new Exception(); + + [Ignore] + public TestOnlyIgnoredCtor(string name) => throw new Exception(); + } + + [Test] + public void TestRegisterValidIgnoreAttribute() + { + IocContainer container = new(); + container.Register(r => r.Add()); + + container.RegisterIgnoreConstructorAttribute(); + + ITest test = container.Resolve("name"); + Assert.IsInstanceOf(test); + } + + [Test] + public void TestRegisterInvalidIgnoreAttribute() + { + IocContainer container = new(); + Assert.Throws>(() => container.RegisterIgnoreConstructorAttribute()); + } + + [Test] + public void TestResolveWithOnlyIgnoredCtors() + { + IocContainer container = new(); + container.Register(r => r.Add()); + + container.RegisterIgnoreConstructorAttribute(); + + Assert.Throws(() => container.Resolve("name")); + } +} \ No newline at end of file