diff --git a/src/Splat.Common.Test/IViewModelOne.cs b/src/Splat.Common.Test/IViewModelOne.cs new file mode 100644 index 000000000..d0de11aa5 --- /dev/null +++ b/src/Splat.Common.Test/IViewModelOne.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// 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 full license information. + +namespace Splat.Common.Test +{ + /// + /// Interface for ViewModelOne. + /// + public interface IViewModelOne + { + } +} diff --git a/src/Splat.Common.Test/ViewModelOne.cs b/src/Splat.Common.Test/ViewModelOne.cs index 56babd24d..f2da11f66 100644 --- a/src/Splat.Common.Test/ViewModelOne.cs +++ b/src/Splat.Common.Test/ViewModelOne.cs @@ -8,7 +8,13 @@ namespace Splat.Common.Test /// /// View Model One. /// - public class ViewModelOne + public class ViewModelOne : IViewModelOne { + /// + /// Initializes a new instance of the class. + /// + public ViewModelOne() + { + } } } diff --git a/src/Splat.Microsoft.Extensions.DependencyInjection/MicrosoftDependencyResolver.cs b/src/Splat.Microsoft.Extensions.DependencyInjection/MicrosoftDependencyResolver.cs index 7a393e659..b7348d478 100644 --- a/src/Splat.Microsoft.Extensions.DependencyInjection/MicrosoftDependencyResolver.cs +++ b/src/Splat.Microsoft.Extensions.DependencyInjection/MicrosoftDependencyResolver.cs @@ -115,7 +115,7 @@ public virtual IEnumerable GetServices(Type serviceType, string? contrac services = dic? .GetFactories(contract) .Select(f => f()) - ?? Enumerable.Empty(); + ?? Array.Empty(); } return services; @@ -370,7 +370,7 @@ public bool TryRemoveContract(string contract) => public IEnumerable> GetFactories(string contract) => _dictionary.TryGetValue(contract, out var collection) ? collection ?? Enumerable.Empty>() - : Enumerable.Empty>(); + : Array.Empty>(); public void AddFactory(string contract, Func factory) => _dictionary.AddOrUpdate(contract, _ => new List> { factory }, (_, list) => diff --git a/src/Splat.SimpleInjector/SimpleInjectorDependencyResolver.cs b/src/Splat.SimpleInjector/SimpleInjectorDependencyResolver.cs index 1d88f6161..92452ed7a 100644 --- a/src/Splat.SimpleInjector/SimpleInjectorDependencyResolver.cs +++ b/src/Splat.SimpleInjector/SimpleInjectorDependencyResolver.cs @@ -66,7 +66,7 @@ public IEnumerable GetServices(Type serviceType, string? contract = null return new[] { registration.GetInstance() }; } - return Enumerable.Empty(); + return Array.Empty(); } } diff --git a/src/Splat.Tests/API/ApiApprovalTests.SplatProject.net5.0.approved.txt b/src/Splat.Tests/API/ApiApprovalTests.SplatProject.net5.0.approved.txt index c11747f59..f53faf455 100644 --- a/src/Splat.Tests/API/ApiApprovalTests.SplatProject.net5.0.approved.txt +++ b/src/Splat.Tests/API/ApiApprovalTests.SplatProject.net5.0.approved.txt @@ -163,6 +163,8 @@ namespace Splat public static System.Collections.Generic.IEnumerable GetServices(this Splat.IReadonlyDependencyResolver resolver, string? contract = null) { } public static void Register(this Splat.IMutableDependencyResolver resolver, System.Func factory, string? contract = null) where T : notnull { } + public static void Register(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } public static void RegisterConstant(this Splat.IMutableDependencyResolver resolver, object value, System.Type serviceType, string? contract = null) { } public static void RegisterConstant(this Splat.IMutableDependencyResolver resolver, T value, string? contract = null) where T : notnull { } @@ -568,6 +570,23 @@ namespace Splat public static System.Tuple DivideWithPadding(this System.Drawing.RectangleF value, float sliceAmount, float padding, Splat.RectEdge fromEdge) { } public static System.Drawing.RectangleF InvertWithin(this System.Drawing.RectangleF value, System.Drawing.RectangleF containingRect) { } } + public static class ResolverMixins + { + public static Splat.IMutableDependencyResolver RegisterAnd(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } + public static Splat.IMutableDependencyResolver RegisterAnd(this Splat.IMutableDependencyResolver resolver, System.Func factory, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterAnd(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } + public static Splat.IMutableDependencyResolver RegisterAnd(this Splat.IMutableDependencyResolver resolver, System.Func factory, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterConstantAnd(this Splat.IMutableDependencyResolver resolver, object value, System.Type serviceType, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterConstantAnd(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } + public static Splat.IMutableDependencyResolver RegisterConstantAnd(this Splat.IMutableDependencyResolver resolver, T value, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterLazySingletonAnd(this Splat.IMutableDependencyResolver resolver, System.Func valueFactory, System.Type serviceType, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterLazySingletonAnd(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } + public static Splat.IMutableDependencyResolver RegisterLazySingletonAnd(this Splat.IMutableDependencyResolver resolver, System.Func valueFactory, string? contract = null) { } + } public static class ServiceLocationInitialization { public static void InitializeSplat(this Splat.IMutableDependencyResolver resolver) { } diff --git a/src/Splat.Tests/API/ApiApprovalTests.SplatProject.netcoreapp3.1.approved.txt b/src/Splat.Tests/API/ApiApprovalTests.SplatProject.netcoreapp3.1.approved.txt index 2b7497bd6..fee459022 100644 --- a/src/Splat.Tests/API/ApiApprovalTests.SplatProject.netcoreapp3.1.approved.txt +++ b/src/Splat.Tests/API/ApiApprovalTests.SplatProject.netcoreapp3.1.approved.txt @@ -163,6 +163,8 @@ namespace Splat public static System.Collections.Generic.IEnumerable GetServices(this Splat.IReadonlyDependencyResolver resolver, string? contract = null) { } public static void Register(this Splat.IMutableDependencyResolver resolver, System.Func factory, string? contract = null) where T : notnull { } + public static void Register(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } public static void RegisterConstant(this Splat.IMutableDependencyResolver resolver, object value, System.Type serviceType, string? contract = null) { } public static void RegisterConstant(this Splat.IMutableDependencyResolver resolver, T value, string? contract = null) where T : notnull { } @@ -568,6 +570,23 @@ namespace Splat public static System.Tuple DivideWithPadding(this System.Drawing.RectangleF value, float sliceAmount, float padding, Splat.RectEdge fromEdge) { } public static System.Drawing.RectangleF InvertWithin(this System.Drawing.RectangleF value, System.Drawing.RectangleF containingRect) { } } + public static class ResolverMixins + { + public static Splat.IMutableDependencyResolver RegisterAnd(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } + public static Splat.IMutableDependencyResolver RegisterAnd(this Splat.IMutableDependencyResolver resolver, System.Func factory, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterAnd(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } + public static Splat.IMutableDependencyResolver RegisterAnd(this Splat.IMutableDependencyResolver resolver, System.Func factory, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterConstantAnd(this Splat.IMutableDependencyResolver resolver, object value, System.Type serviceType, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterConstantAnd(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } + public static Splat.IMutableDependencyResolver RegisterConstantAnd(this Splat.IMutableDependencyResolver resolver, T value, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterLazySingletonAnd(this Splat.IMutableDependencyResolver resolver, System.Func valueFactory, System.Type serviceType, string? contract = null) { } + public static Splat.IMutableDependencyResolver RegisterLazySingletonAnd(this Splat.IMutableDependencyResolver resolver, string? contract = null) + where T : new() { } + public static Splat.IMutableDependencyResolver RegisterLazySingletonAnd(this Splat.IMutableDependencyResolver resolver, System.Func valueFactory, string? contract = null) { } + } public static class ServiceLocationInitialization { public static void InitializeSplat(this Splat.IMutableDependencyResolver resolver) { } diff --git a/src/Splat.Tests/LocatorSerialRegisterTests.cs b/src/Splat.Tests/LocatorSerialRegisterTests.cs new file mode 100644 index 000000000..ca5f7f1f7 --- /dev/null +++ b/src/Splat.Tests/LocatorSerialRegisterTests.cs @@ -0,0 +1,370 @@ +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// 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 full license information. + +using System; + +using FluentAssertions; +using Splat.Tests.Mocks; +using Xunit; + +namespace Splat.Tests +{ + /// + /// Tests to confirm that the locator is working. + /// + public class LocatorSerialRegisterTests + { + /// + /// Tests if the registrations are not empty on no external registrations. + /// + [Fact] + public void InitializeSplat_RegistrationsNotEmptyNoRegistrations() + { + // this is using the internal constructor + var testLocator = new InternalLocator(); + var logManager = testLocator.Current.GetService(typeof(ILogManager)); + var logger = testLocator.Current.GetService(typeof(ILogger)); + + Assert.NotNull(logManager); + Assert.NotNull(logger); + + Assert.IsType(logger); + Assert.IsType(logManager); + } + + /// + /// Tests that if we use a contract it returns null entries for that type. + /// + [Fact] + public void InitializeSplat_ContractRegistrationsNullNoRegistration() + { + var testLocator = new InternalLocator(); + var logManager = testLocator.Current.GetService(typeof(ILogManager), "test"); + var logger = testLocator.Current.GetService(typeof(ILogger), "test"); + + Assert.Null(logManager); + Assert.Null(logger); + } + + /// + /// Tests using the extension methods that the retrieving of the default InitializeSplat() still work. + /// + [Fact] + public void InitializeSplat_ExtensionMethodsNotNull() + { + var testLocator = new InternalLocator(); + var logManager = testLocator.Current.GetService(); + var logger = testLocator.Current.GetService(); + + Assert.NotNull(logManager); + Assert.NotNull(logger); + + Assert.IsType(logger); + Assert.IsType(logManager); + } + + /// + /// Tests to make sure that the locator's fire the resolver changed notifications. + /// + [Fact] + public void WithoutSuppress_NotificationsHappen() + { + var testLocator = new InternalLocator(); + var originalLocator = testLocator.Internal; + + int numberNotifications = 0; + Action notificationAction = () => numberNotifications++; + + testLocator.RegisterResolverCallbackChanged(notificationAction); + + testLocator.SetLocator(new ModernDependencyResolver()); + testLocator.SetLocator(new ModernDependencyResolver()); + + // 2 for the changes, 1 for the callback being immediately called. + Assert.Equal(3, numberNotifications); + + testLocator.SetLocator(originalLocator); + } + + /// + /// Tests to make sure that the locator's don't fire the resolver changed notifications if they are suppressed. + /// + [Fact] + public void WithSuppression_NotificationsDontHappen() + { + var testLocator = new InternalLocator(); + var originalLocator = testLocator.Internal; + + using (testLocator.SuppressResolverCallbackChangedNotifications()) + { + int numberNotifications = 0; + Action notificationAction = () => numberNotifications++; + + testLocator.RegisterResolverCallbackChanged(notificationAction); + + testLocator.SetLocator(new ModernDependencyResolver()); + testLocator.SetLocator(new ModernDependencyResolver()); + + Assert.Equal(0, numberNotifications); + + testLocator.SetLocator(originalLocator); + } + } + + /// + /// Tests to make sure that the locator's don't fire the resolver changed notifications if we use WithResolver(). + /// + [Fact] + public void WithResolver_NotificationsDontHappen() + { + int numberNotifications = 0; + Action notificationAction = () => numberNotifications++; + + var testLocator = new InternalLocator(); + testLocator.RegisterResolverCallbackChanged(notificationAction); + + using (testLocator.Internal.WithResolver()) + { + using (testLocator.Internal.WithResolver()) + { + } + } + + // 1 due to the fact the callback is called when we register. + Assert.Equal(1, numberNotifications); + } + + /// + /// Tests to make sure that the locator's don't fire the resolver changed notifications if we use WithResolver(). + /// + [Fact] + public void WithResolver_NotificationsNotSuppressedHappen() + { + int numberNotifications = 0; + Action notificationAction = () => numberNotifications++; + + Locator.RegisterResolverCallbackChanged(notificationAction); + + using (Locator.GetLocator().WithResolver(false)) + { + using (Locator.GetLocator().WithResolver(false)) + { + } + } + + // 1 due to the fact the callback is called when we register. + // 2 for, 1 for change to resolver, 1 for change back + // 2 for, 1 for change to resolver, 1 for change back + Assert.Equal(5, numberNotifications); + } + + /// + /// Tests to make sure that the unregister all functions correctly. + /// This is a test when there are values registered. + /// + [Fact] + public void ModernDependencyResolver_UnregisterAll_WithValuesWorks() + { + var currentMutable = new ModernDependencyResolver(); + + var dummy1 = new DummyObjectClass1(); + var dummy2 = new DummyObjectClass2(); + var dummy3 = new DummyObjectClass3(); + + var testContracts = new[] { string.Empty, "test" }; + + foreach (var testContract in testContracts) + { + currentMutable.RegisterConstantAnd(dummy1, testContract) + .RegisterConstantAnd(dummy2, testContract) + .RegisterConstant(dummy3, testContract); + } + + foreach (var testContract in testContracts) + { + var items = currentMutable.GetServices(testContract); + + items.Should().BeEquivalentTo(new IDummyInterface[] { dummy1, dummy2, dummy3 }); + + currentMutable.UnregisterAll(testContract); + + items = currentMutable.GetServices(testContract); + + items.Should().BeEmpty(); + } + } + + /// + /// Tests to make sure that the unregister all functions correctly. + /// This is a test when there are values not registered. + /// + [Fact] + public void ModernDependencyResolver_UnregisterAll_NoValuesWorks() + { + var currentMutable = new ModernDependencyResolver(); + + var items = currentMutable.GetServices(); + + items.Should().BeEmpty(); + + currentMutable.UnregisterAll(); + + items = currentMutable.GetServices(); + + items.Should().BeEmpty(); + } + + /// + /// Tests tomake sure that the unregister current functions correctly. + /// This is a test when there are values registered. + /// + [Fact] + public void ModernDependencyResolver_ConstantUnregisterCurrent_WithValuesWorks() + { + var dummy1 = new DummyObjectClass1(); + var dummy2 = new DummyObjectClass2(); + var dummy3 = new DummyObjectClass3(); + + var currentMutable = new ModernDependencyResolver(); + + var testContracts = new[] { string.Empty, "test" }; + + foreach (var testContract in testContracts) + { + currentMutable.RegisterConstantAnd(dummy1, testContract) + .RegisterConstantAnd(dummy2, testContract) + .RegisterConstant(dummy3, testContract); + } + + foreach (var testContract in testContracts) + { + var items = currentMutable.GetServices(testContract); + + items.Should().BeEquivalentTo(new IDummyInterface[] { dummy1, dummy2, dummy3 }); + + currentMutable.UnregisterCurrent(testContract); + + items = currentMutable.GetServices(testContract); + + items.Should().BeEquivalentTo(new IDummyInterface[] { dummy1, dummy2 }); + } + } + + /// + /// Tests tomake sure that the unregister current functions correctly. + /// This is a test when there are values registered. + /// + [Fact] + public void ModernDependencyResolver_UnregisterCurrent_WithValuesWorks() + { + var currentMutable = new ModernDependencyResolver(); + + var testContracts = new[] { string.Empty, "test" }; + + foreach (var testContract in testContracts) + { + currentMutable.RegisterAnd(testContract) + .RegisterAnd(testContract) + .Register(testContract); + } + + foreach (var testContract in testContracts) + { + var items = currentMutable.GetServices(testContract); + + items.Should().HaveCount(3); + + currentMutable.UnregisterCurrent(testContract); + + items = currentMutable.GetServices(testContract); + + items.Should().HaveCount(2); + } + } + + /// + /// Tests to make sure that the unregister all functions correctly. + /// This is a test when there are values not registered. + /// + [Fact] + public void ModernDependencyResolver_UnregisterCurrent_NoValuesWorks() + { + var currentMutable = new ModernDependencyResolver(); + var items = currentMutable.GetServices(); + + items.Should().BeEmpty(); + + currentMutable.UnregisterCurrent(); + + items = currentMutable.GetServices(); + + items.Should().BeEmpty(); + } + + /// + /// Tests to make sure that the unregister all functions correctly. + /// This is a test when there are values not registered. + /// + [Fact] + public void FuncDependencyResolver_UnregisterAll() + { + bool unregisterAllCalled = false; + Type type = null; + string contract = null; + + var currentMutable = new FuncDependencyResolver( + (funcType, funcContract) => Array.Empty(), + unregisterAll: (passedType, passedContract) => + { + unregisterAllCalled = true; + contract = passedContract; + type = passedType; + }); + + currentMutable.UnregisterAll(); + type.Should().Be(typeof(IDummyInterface)); + contract.Should().BeNull(); + unregisterAllCalled.Should().BeTrue(); + + unregisterAllCalled = false; + currentMutable.UnregisterAll("test"); + type.Should().Be(typeof(IEnableLogger)); + contract.Should().Be("test"); + unregisterAllCalled.Should().BeTrue(); + } + + /// + /// Tests tomake sure that the unregister current functions correctly. + /// This is a test when there are values registered. + /// + [Fact] + public void FuncDependencyResolver_UnregisterCurrent() + { + bool unregisterAllCalled = false; + Type type = null; + string contract = null; + + var currentMutable = new FuncDependencyResolver( + (funcType, funcContract) => Array.Empty(), + unregisterCurrent: (passedType, passedContract) => + { + unregisterAllCalled = true; + contract = passedContract; + type = passedType; + }); + + currentMutable.UnregisterCurrent(); + type.Should().Be(typeof(IDummyInterface)); + contract.Should().BeNull(); + unregisterAllCalled.Should().BeTrue(); + + unregisterAllCalled = false; + currentMutable.UnregisterCurrent("test"); + type.Should().Be(typeof(IEnableLogger)); + contract.Should().Be("test"); + unregisterAllCalled.Should().BeTrue(); + } + } +} diff --git a/src/Splat.Tests/ServiceLocation/BaseDependencyResolverTests.cs b/src/Splat.Tests/ServiceLocation/BaseDependencyResolverTests.cs index 1f4f036da..20516c2e6 100644 --- a/src/Splat.Tests/ServiceLocation/BaseDependencyResolverTests.cs +++ b/src/Splat.Tests/ServiceLocation/BaseDependencyResolverTests.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Text; +using Splat.Common.Test; using Splat.NLog; using Xunit; @@ -159,6 +160,57 @@ public void ILogManager_Resolvable() Assert.NotNull(lm); } + /// + /// Nulls the resolver tests. + /// + [Fact] + public void NullResolverTests() + { + IReadonlyDependencyResolver resolver = default; + IMutableDependencyResolver resolver1 = default; + IDependencyResolver resolver2 = default; + Assert.Throws(() => resolver.GetService()); + Assert.Throws(() => resolver.GetServices()); + Assert.Throws(() => resolver1.ServiceRegistrationCallback(typeof(ILogManager), (IDisposable d) => { d.Dispose(); })); + Assert.Throws(() => resolver2.WithResolver().Dispose()); + Assert.Throws(() => resolver1.Register(() => new DefaultLogManager())); + Assert.Throws(() => resolver1.RegisterConstant(new DefaultLogManager())); + Assert.Throws(() => resolver1.RegisterLazySingleton(() => new DefaultLogManager(), typeof(ILogManager))); + Assert.Throws(() => resolver1.RegisterLazySingletonAnd(() => new DefaultLogManager(), typeof(ILogManager))); + Assert.Throws(() => resolver1.RegisterLazySingleton(() => new DefaultLogManager())); + Assert.Throws(() => resolver1.RegisterLazySingletonAnd("eight")); + Assert.Throws(() => resolver1.RegisterLazySingletonAnd(() => new DefaultLogManager(), "seven")); + Assert.Throws(() => resolver1.UnregisterCurrent()); + Assert.Throws(() => resolver1.UnregisterAll()); + Assert.Throws(() => resolver1.RegisterAnd()); + Assert.Throws(() => resolver1.RegisterAnd(() => new DefaultLogManager())); + Assert.Throws(() => resolver1.RegisterAnd(() => new ViewModelOne())); + Assert.Throws(() => resolver1.Register()); + Assert.Throws(() => resolver1.RegisterConstantAnd(new ViewModelOne())); + Assert.Throws(() => resolver1.RegisterConstantAnd(new ViewModelOne(), typeof(ViewModelOne))); + Assert.Throws(() => resolver1.RegisterConstantAnd()); + } + + /// + /// Registers the and tests. + /// + [Fact] + public void RegisterAndTests() + { + var resolver = GetDependencyResolver(); + Assert.Throws(() => resolver.RegisterAnd(null)); + resolver.RegisterAnd("one") + .RegisterAnd("two") + .RegisterAnd(() => new DefaultLogManager(), "three") + .RegisterAnd(() => new ViewModelOne(), "four") + .RegisterConstantAnd("five") + .RegisterConstantAnd(new ViewModelOne(), typeof(ViewModelOne), "six") + .RegisterLazySingletonAnd(() => new DefaultLogManager(), typeof(ILogManager), "seven") + .RegisterLazySingletonAnd("eight") + .RegisterLazySingletonAnd(() => new DefaultLogManager(), "seven") + .Register(); + } + /// /// Gets an instance of a dependency resolver to test. /// diff --git a/src/Splat.Tests/Splat.Tests.csproj b/src/Splat.Tests/Splat.Tests.csproj index 88b9f9b32..18cfb75db 100644 --- a/src/Splat.Tests/Splat.Tests.csproj +++ b/src/Splat.Tests/Splat.Tests.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Splat/ServiceLocation/DependencyResolverMixins.cs b/src/Splat/ServiceLocation/DependencyResolverMixins.cs index be913d30e..77a7714c4 100644 --- a/src/Splat/ServiceLocation/DependencyResolverMixins.cs +++ b/src/Splat/ServiceLocation/DependencyResolverMixins.cs @@ -115,6 +115,24 @@ public static void Register(this IMutableDependencyResolver resolver, Func resolver.Register(() => factory(), typeof(T), contract); } + /// + /// Registers a factory for the given . + /// + /// The type to register as. + /// The service type to register for. + /// The resolver to register the service type with. + /// A optional contract value which will indicates to only generate the value if this contract is specified. + public static void Register(this IMutableDependencyResolver resolver, string? contract = null) + where T : new() + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + resolver.Register(() => new T(), typeof(TAs), contract); + } + /// /// Registers a constant value which will always return the specified object instance. /// diff --git a/src/Splat/ServiceLocation/FuncDependencyResolver.cs b/src/Splat/ServiceLocation/FuncDependencyResolver.cs index 4b898ad4e..440c890a2 100644 --- a/src/Splat/ServiceLocation/FuncDependencyResolver.cs +++ b/src/Splat/ServiceLocation/FuncDependencyResolver.cs @@ -51,7 +51,7 @@ public FuncDependencyResolver( /// public object? GetService(Type serviceType, string? contract = null) { - return (GetServices(serviceType, contract) ?? Enumerable.Empty()).LastOrDefault(); + return (GetServices(serviceType, contract) ?? Array.Empty()).LastOrDefault(); } /// diff --git a/src/Splat/ServiceLocation/ModernDependencyResolver.cs b/src/Splat/ServiceLocation/ModernDependencyResolver.cs index a4cdece1b..1515c06f7 100644 --- a/src/Splat/ServiceLocation/ModernDependencyResolver.cs +++ b/src/Splat/ServiceLocation/ModernDependencyResolver.cs @@ -133,13 +133,13 @@ public IEnumerable GetServices(Type serviceType, string? contract = null { if (_registry is null) { - return Enumerable.Empty(); + return Array.Empty(); } var pair = GetKey(serviceType, contract); if (!_registry.ContainsKey(pair)) { - return Enumerable.Empty(); + return Array.Empty(); } return _registry[pair].Select(x => x()).ToList(); diff --git a/src/Splat/ServiceLocation/ResolverMixins.cs b/src/Splat/ServiceLocation/ResolverMixins.cs new file mode 100644 index 000000000..4f9265c50 --- /dev/null +++ b/src/Splat/ServiceLocation/ResolverMixins.cs @@ -0,0 +1,223 @@ +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// 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 full license information. + +using System; +using System.Threading; + +namespace Splat +{ + /// + /// Resolver Mixins. + /// + public static class ResolverMixins + { + /// + /// Registers a factory for the given . + /// + /// The service type to register for. + /// The resolver to register the service type with. + /// A optional contract value which will indicates to only generate the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterAnd(this IMutableDependencyResolver resolver, string? contract = null) + where T : new() + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + resolver.Register(() => new T(), typeof(T), contract); + return resolver; + } + + /// + /// Registers a factory for the given . + /// + /// The service type to register for. + /// The resolver to register the service type with. + /// A factory method for generating a object of the specified type. + /// A optional contract value which will indicates to only generate the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterAnd(this IMutableDependencyResolver resolver, Func factory, string? contract = null) + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + if (factory is null) + { + throw new ArgumentNullException(nameof(factory)); + } + + resolver.Register(() => factory()!, typeof(T), contract); + return resolver; + } + + /// + /// Registers a factory for the given . + /// + /// The type to register as. + /// The service type to register for. + /// The resolver to register the service type with. + /// A optional contract value which will indicates to only generate the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterAnd(this IMutableDependencyResolver resolver, string? contract = null) + where T : new() + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + resolver.Register(() => new T(), typeof(TAs), contract); + return resolver; + } + + /// + /// Registers a factory for the given . + /// + /// The type to register as. + /// The service type to register for. + /// The resolver to register the service type with. + /// A factory method for generating a object of the specified type. + /// A optional contract value which will indicates to only generate the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterAnd(this IMutableDependencyResolver resolver, Func factory, string? contract = null) + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + if (factory is null) + { + throw new ArgumentNullException(nameof(factory)); + } + + resolver.Register(() => factory()!, typeof(TAs), contract); + return resolver; + } + + /// + /// Registers a constant value which will always return the specified object instance. + /// + /// The resolver to register the service type with. + /// The specified instance to always return. + /// The type of service to register. + /// A optional contract value which will indicates to only return the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterConstantAnd(this IMutableDependencyResolver resolver, object value, Type serviceType, string? contract = null) + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + resolver.Register(() => value, serviceType, contract); + return resolver; + } + + /// + /// Registers a constant value which will always return the specified object instance. + /// + /// The service type to register for. + /// The resolver to register the service type with. + /// A optional contract value which will indicates to only return the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterConstantAnd(this IMutableDependencyResolver resolver, string? contract = null) + where T : new() + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + var value = new T(); + return resolver.RegisterAnd(() => value, contract); + } + + /// + /// Registers a constant value which will always return the specified object instance. + /// + /// The service type to register for. + /// The resolver to register the service type with. + /// The specified instance to always return. + /// A optional contract value which will indicates to only return the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterConstantAnd(this IMutableDependencyResolver resolver, T value, string? contract = null) + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + return resolver.RegisterAnd(() => value, contract); + } + + /// + /// Registers a lazy singleton value which will always return the specified object instance once created. + /// The value is only generated once someone requests the service from the resolver. + /// + /// The resolver to register the service type with. + /// A factory method for generating a object of the specified type. + /// The type of service to register. + /// A optional contract value which will indicates to only return the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterLazySingletonAnd(this IMutableDependencyResolver resolver, Func valueFactory, Type serviceType, string? contract = null) + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + var val = new Lazy(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication); + resolver.Register(() => val.Value, serviceType, contract); + return resolver; + } + + /// + /// Registers a lazy singleton value which will always return the specified object instance once created. + /// The value is only generated once someone requests the service from the resolver. + /// + /// The service type to register for. + /// The resolver to register the service type with. + /// A optional contract value which will indicates to only return the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterLazySingletonAnd(this IMutableDependencyResolver resolver, string? contract = null) + where T : new() + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + var val = new Lazy(() => new T(), LazyThreadSafetyMode.ExecutionAndPublication); + resolver.Register(() => val.Value, typeof(T), contract); + return resolver; + } + + /// + /// Registers a lazy singleton value which will always return the specified object instance once created. + /// The value is only generated once someone requests the service from the resolver. + /// + /// The service type to register for. + /// The resolver to register the service type with. + /// A factory method for generating a object of the specified type. + /// A optional contract value which will indicates to only return the value if this contract is specified. + /// The resolver. + public static IMutableDependencyResolver RegisterLazySingletonAnd(this IMutableDependencyResolver resolver, Func valueFactory, string? contract = null) + { + if (resolver is null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + var val = new Lazy(() => valueFactory()!, LazyThreadSafetyMode.ExecutionAndPublication); + resolver.Register(() => val.Value, typeof(T), contract); + return resolver; + } + } +}