From e3a8d311e70da7639c14764d8c9eac6cb350633d Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Wed, 7 Feb 2018 15:47:03 -0700 Subject: [PATCH 1/6] Let user set delegate to resolve handlers, effects, and services; closes #1739 --- .../DependencyResolutionTests.cs | 166 ++++++++++++++++++ .../Xamarin.Forms.Core.UnitTests.csproj | 7 +- Xamarin.Forms.Core/DependencyService.cs | 17 ++ Xamarin.Forms.Core/Effect.cs | 13 +- Xamarin.Forms.Core/Registrar.cs | 53 +++++- .../Registrar+ResolveDelegate.xml | 31 ++++ .../Xamarin.Forms.Internals/Registrar.xml | 38 ++++ .../Xamarin.Forms/DependencyService.xml | 28 +++ docs/Xamarin.Forms.Core/index.xml | 1 + 9 files changed, 344 insertions(+), 10 deletions(-) create mode 100644 Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs create mode 100644 docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar+ResolveDelegate.xml diff --git a/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs b/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs new file mode 100644 index 00000000000..0adb2cda1ce --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class DependencyResolutionTests : BaseTestFixture + { + class MockElement { } + + class MockElementRenderer : IRegisterable { } + + class MockRendererWithParam : IRegisterable + { + readonly object _aParameter; + + public MockRendererWithParam(object aParameter) + { + _aParameter = aParameter ?? throw new ArgumentNullException(nameof(aParameter)); + } + } + + class MockEffect : Effect + { + protected override void OnAttached() + { + throw new NotImplementedException(); + } + + protected override void OnDetached() + { + throw new NotImplementedException(); + } + } + + interface IMockService { } + + class MockServiceImpl : IMockService { } + + // ReSharper disable once ClassNeverInstantiated.Local + // (Just testing the type registration, don't need an instance) + class MockServiceImpl2 : IMockService { } + + class MockContainer + { + readonly Dictionary _services; + readonly Dictionary> _factories; + + public MockContainer() + { + _services = new Dictionary(); + _factories = new Dictionary>(); + } + + public void Register(Type type, object result) + { + _services.Add(type, result); + } + + public void Register(Type type, Func factory) + { + _factories.Add(type, factory); + } + + public object Resolve(Type type, params object[] args) + { + if (_services.ContainsKey(type)) + { + return _services[type]; + } + + if (_factories.ContainsKey(type)) + { + return _factories[type].Invoke(args[0]); + } + + return null; + } + } + + MockContainer _container; + + [SetUp] + public void SetUp() + { + Internals.Registrar.Reset(); + + _container = new MockContainer(); + + object ResolveDelegate(Type type, object[] args) => _container.Resolve(type, args); + + Internals.Registrar.ResolveUsing(ResolveDelegate); + } + + [Test] + public void GetHandlerFromContainer() + { + Internals.Registrar.Registered.Register(typeof(MockElement), typeof(MockElementRenderer)); + var renderer = new MockElementRenderer(); + _container.Register(typeof(MockElementRenderer), renderer); + var result = Internals.Registrar.Registered.GetHandler(typeof(MockElement)); + var typedRenderer = (MockElementRenderer)result; + + Assert.That(typedRenderer, Is.SameAs(renderer)); + } + + [Test] + public void GetEffectFromContainer() + { + string effectName = "anEffect"; + Internals.Registrar.Effects[effectName] = typeof(MockEffect); + var effect = new MockEffect(); + _container.Register(typeof(MockEffect), effect); + var result = Effect.Resolve(effectName); + + Assert.That(result, Is.SameAs(effect)); + } + + [Test] + public void GetServiceFromContainer() + { + MockServiceImpl impl = new MockServiceImpl(); + _container.Register(typeof(IMockService), impl); + DependencyService.Register(); + var result = DependencyService.Resolve(); + + Assert.That(result, Is.SameAs(impl)); + } + + [Test] + public void PreferServiceTypeFromContainer() + { + MockServiceImpl impl = new MockServiceImpl(); + _container.Register(typeof(IMockService), impl); + DependencyService.Register(); + var result = DependencyService.Resolve(); + + Assert.That(result, Is.SameAs(impl)); + } + + [Test] + public void FallbackOnDependencyServiceIfNotInContainer() + { + DependencyService.Register(); + var result = DependencyService.Resolve(); + + Assert.That(result, Is.Not.Null); + } + + [Test] + public void HandlerWithParameter() + { + Internals.Registrar.Registered.Register(typeof(MockElement), typeof(MockRendererWithParam)); + + _container.Register(typeof(MockRendererWithParam), (o) => new MockRendererWithParam(o)); + + var context = "Pretend this is an Android context"; + + var result = Internals.Registrar.Registered.GetHandler(typeof(MockElement), context); + var typedRenderer = (MockRendererWithParam)result; + + Assert.That(typedRenderer, Is.InstanceOf(typeof(MockRendererWithParam))); + } + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj b/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj index d0b73522d12..32613d683b6 100644 --- a/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj +++ b/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj @@ -73,6 +73,7 @@ + @@ -225,7 +226,5 @@ Images/crimson.jpg - - - - + + \ No newline at end of file diff --git a/Xamarin.Forms.Core/DependencyService.cs b/Xamarin.Forms.Core/DependencyService.cs index 2e3c1665db2..4d4905e1018 100644 --- a/Xamarin.Forms.Core/DependencyService.cs +++ b/Xamarin.Forms.Core/DependencyService.cs @@ -13,6 +13,23 @@ public static class DependencyService static readonly List DependencyTypes = new List(); static readonly Dictionary DependencyImplementations = new Dictionary(); + public static T Resolve(DependencyFetchTarget fallbackFetchTarget = DependencyFetchTarget.GlobalInstance) where T : class + { + T result = null; + + if (Internals.Registrar.Resolver != null) + { + result = Internals.Registrar.Resolver.Invoke(typeof(T)) as T; + } + + if (result != null) + { + return result; + } + + return Get(fallbackFetchTarget); + } + public static T Get(DependencyFetchTarget fetchTarget = DependencyFetchTarget.GlobalInstance) where T : class { Initialize(); diff --git a/Xamarin.Forms.Core/Effect.cs b/Xamarin.Forms.Core/Effect.cs index 61b62c0b765..0fdc75620e8 100644 --- a/Xamarin.Forms.Core/Effect.cs +++ b/Xamarin.Forms.Core/Effect.cs @@ -20,11 +20,18 @@ internal Effect() public static Effect Resolve(string name) { - Type effectType; Effect result = null; - if (Internals.Registrar.Effects.TryGetValue(name, out effectType)) + if (Internals.Registrar.Effects.TryGetValue(name, out Type effectType)) { - result = (Effect)Activator.CreateInstance(effectType); + if (Internals.Registrar.Resolver != null) + { + result = Internals.Registrar.Resolver.Invoke(effectType) as Effect; + } + + if (result == null) + { + result = (Effect)Activator.CreateInstance(effectType); + } } if (result == null) diff --git a/Xamarin.Forms.Core/Registrar.cs b/Xamarin.Forms.Core/Registrar.cs index f4775afe943..c90031d7434 100644 --- a/Xamarin.Forms.Core/Registrar.cs +++ b/Xamarin.Forms.Core/Registrar.cs @@ -12,6 +12,7 @@ internal static class Registrar internal static void RegisterAll(Type[] attrTypes) => Internals.Registrar.RegisterAll(attrTypes); } } + namespace Xamarin.Forms.Internals { [EditorBrowsable(EditorBrowsableState.Never)] @@ -33,7 +34,18 @@ internal TRegistrable GetHandler(Type type) if (handlerType == null) return null; - object handler = Activator.CreateInstance(handlerType); + object handler = null; + + if (Registrar.Resolver != null) + { + handler = Registrar.Resolver.Invoke(handlerType); + } + + if (handler == null) + { + handler = Activator.CreateInstance(handlerType); + } + return (TRegistrable)handler; } @@ -48,12 +60,26 @@ internal TRegistrable GetHandler(Type type, params object[] args) if (handlerType == null) return null; + object handler = null; + + if (Registrar.Resolver != null) + { + //var argsCopy = new object[args.Length]; + //args.CopyTo(argsCopy, 0); + handler = Registrar.Resolver.Invoke(handlerType, args); + } + + if (handler != null) + { + return (TRegistrable)handler; + } + // This is by no means a general solution to matching with the correct constructor, but it'll // do for finding Android renderers which need Context (vs older custom renderers which may still use // parameterless constructors) if (handlerType.GetTypeInfo().DeclaredConstructors.Any(info => info.GetParameters().Length == args.Length)) { - object handler = Activator.CreateInstance(handlerType, args); + handler = Activator.CreateInstance(handlerType, args); return (TRegistrable)handler; } @@ -171,7 +197,7 @@ static Registrar() public static IEnumerable ExtraAssemblies { get; set; } - public static Registrar Registered { get; } + public static Registrar Registered { get; internal set; } public static void RegisterAll(Type[] attrTypes) { @@ -228,5 +254,26 @@ public static void RegisterAll(Type[] attrTypes) DependencyService.Initialize(assemblies); } + + public delegate object ResolveDelegate(Type type, params object[] args); + + internal static ResolveDelegate Resolver { get; set; } + + public static void ResolveUsing(ResolveDelegate resolveDelegate) + { + Resolver = resolveDelegate; + } + + public static void ResolveUsing(Func resolveFunc) + { + object ResolveDelegate(Type type, object[] args) => resolveFunc.Invoke(type); + Resolver = ResolveDelegate; + } + + internal static void Reset() + { + Registered = new Registrar(); + Resolver = null; + } } } diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar+ResolveDelegate.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar+ResolveDelegate.xml new file mode 100644 index 00000000000..1f401578b3f --- /dev/null +++ b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar+ResolveDelegate.xml @@ -0,0 +1,31 @@ + + + + + Xamarin.Forms.Core + 2.0.0.0 + + + System.Delegate + + + + + + + System.ParamArray + + + + + + System.Object + + + To be added. + To be added. + To be added. + To be added. + To be added. + + diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar.xml index 2968e885047..48dee9ceea6 100644 --- a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar.xml +++ b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar.xml @@ -70,5 +70,43 @@ To be added. + + + + Method + + 2.0.0.0 + + + System.Void + + + + + + To be added. + To be added. + To be added. + + + + + + Method + + 2.0.0.0 + + + System.Void + + + + + + To be added. + To be added. + To be added. + + diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms/DependencyService.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms/DependencyService.xml index b128a2afc97..c98884d157b 100644 --- a/docs/Xamarin.Forms.Core/Xamarin.Forms/DependencyService.xml +++ b/docs/Xamarin.Forms.Core/Xamarin.Forms/DependencyService.xml @@ -115,5 +115,33 @@ To be added. + + + + Method + + 2.0.0.0 + + + T + + + + + ReferenceTypeConstraint + + + + + + + + To be added. + To be added. + To be added. + To be added. + To be added. + + diff --git a/docs/Xamarin.Forms.Core/index.xml b/docs/Xamarin.Forms.Core/index.xml index 9ea674af5b9..069a33d7b38 100644 --- a/docs/Xamarin.Forms.Core/index.xml +++ b/docs/Xamarin.Forms.Core/index.xml @@ -502,6 +502,7 @@ + From e33a440e4f384bb0ea53cdeacfa1fb32ac4093ce Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Mon, 12 Feb 2018 16:37:58 -0700 Subject: [PATCH 2/6] Remove old comments --- Xamarin.Forms.Core/Registrar.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Xamarin.Forms.Core/Registrar.cs b/Xamarin.Forms.Core/Registrar.cs index c90031d7434..7481ac078ed 100644 --- a/Xamarin.Forms.Core/Registrar.cs +++ b/Xamarin.Forms.Core/Registrar.cs @@ -64,8 +64,6 @@ internal TRegistrable GetHandler(Type type, params object[] args) if (Registrar.Resolver != null) { - //var argsCopy = new object[args.Length]; - //args.CopyTo(argsCopy, 0); handler = Registrar.Resolver.Invoke(handlerType, args); } @@ -270,6 +268,7 @@ public static void ResolveUsing(Func resolveFunc) Resolver = ResolveDelegate; } + // Used for testing internal static void Reset() { Registered = new Registrar(); From 2eaa4ba762e97763eb0d9f0d314082bef83424e2 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Tue, 13 Feb 2018 12:30:49 -0700 Subject: [PATCH 3/6] Move dependency-resolution-specific code to a separate class; Remove Reset method; simplify internal API; --- .../DependencyResolutionTests.cs | 4 +- Xamarin.Forms.Core/DependencyResolver.cs | 49 ++++++++++++++++ Xamarin.Forms.Core/DependencyService.cs | 14 +---- Xamarin.Forms.Core/Effect.cs | 10 +--- Xamarin.Forms.Core/Registrar.cs | 57 +------------------ 5 files changed, 55 insertions(+), 79 deletions(-) create mode 100644 Xamarin.Forms.Core/DependencyResolver.cs diff --git a/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs b/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs index 0adb2cda1ce..6df9673beb7 100644 --- a/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs +++ b/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs @@ -84,13 +84,11 @@ public object Resolve(Type type, params object[] args) [SetUp] public void SetUp() { - Internals.Registrar.Reset(); - _container = new MockContainer(); object ResolveDelegate(Type type, object[] args) => _container.Resolve(type, args); - Internals.Registrar.ResolveUsing(ResolveDelegate); + Internals.DependencyResolver.ResolveUsing(ResolveDelegate); } [Test] diff --git a/Xamarin.Forms.Core/DependencyResolver.cs b/Xamarin.Forms.Core/DependencyResolver.cs new file mode 100644 index 00000000000..51e894a46fd --- /dev/null +++ b/Xamarin.Forms.Core/DependencyResolver.cs @@ -0,0 +1,49 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace Xamarin.Forms.Internals +{ + public static class DependencyResolver + { + public delegate object ResolveDelegate(Type type, params object[] args); + + static ResolveDelegate Resolver { get; set; } + + public static void ResolveUsing(ResolveDelegate resolveDelegate) + { + Resolver = resolveDelegate; + } + + public static void ResolveUsing(Func resolveFunc) + { + object ResolveDelegate(Type type, object[] args) => resolveFunc.Invoke(type); + Resolver = ResolveDelegate; + } + + internal static object Resolve(Type type, params object[] args) + { + return Resolver?.Invoke(type, args); + } + + internal static object ForceResolve(Type type, params object[] args) + { + var result = Resolve(type, args); + + if (result != null) return result; + + if (args.Length > 0) + { + // This is by no means a general solution to matching with the correct constructor, but it'll + // do for finding Android renderers which need Context (vs older custom renderers which may still use + // parameterless constructors) + if (type.GetTypeInfo().DeclaredConstructors.Any(info => info.GetParameters().Length == args.Length)) + { + return Activator.CreateInstance(type, args); + } + } + + return Activator.CreateInstance(type); + } + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Core/DependencyService.cs b/Xamarin.Forms.Core/DependencyService.cs index 4d4905e1018..c998c35cad0 100644 --- a/Xamarin.Forms.Core/DependencyService.cs +++ b/Xamarin.Forms.Core/DependencyService.cs @@ -15,19 +15,9 @@ public static class DependencyService public static T Resolve(DependencyFetchTarget fallbackFetchTarget = DependencyFetchTarget.GlobalInstance) where T : class { - T result = null; + var result = DependencyResolver.Resolve(typeof(T)) as T; - if (Internals.Registrar.Resolver != null) - { - result = Internals.Registrar.Resolver.Invoke(typeof(T)) as T; - } - - if (result != null) - { - return result; - } - - return Get(fallbackFetchTarget); + return result ?? Get(fallbackFetchTarget); } public static T Get(DependencyFetchTarget fetchTarget = DependencyFetchTarget.GlobalInstance) where T : class diff --git a/Xamarin.Forms.Core/Effect.cs b/Xamarin.Forms.Core/Effect.cs index 0fdc75620e8..bf607a5d48f 100644 --- a/Xamarin.Forms.Core/Effect.cs +++ b/Xamarin.Forms.Core/Effect.cs @@ -23,15 +23,7 @@ public static Effect Resolve(string name) Effect result = null; if (Internals.Registrar.Effects.TryGetValue(name, out Type effectType)) { - if (Internals.Registrar.Resolver != null) - { - result = Internals.Registrar.Resolver.Invoke(effectType) as Effect; - } - - if (result == null) - { - result = (Effect)Activator.CreateInstance(effectType); - } + result = DependencyResolver.ForceResolve(effectType) as Effect; } if (result == null) diff --git a/Xamarin.Forms.Core/Registrar.cs b/Xamarin.Forms.Core/Registrar.cs index 7481ac078ed..d7b9490a151 100644 --- a/Xamarin.Forms.Core/Registrar.cs +++ b/Xamarin.Forms.Core/Registrar.cs @@ -34,17 +34,7 @@ internal TRegistrable GetHandler(Type type) if (handlerType == null) return null; - object handler = null; - - if (Registrar.Resolver != null) - { - handler = Registrar.Resolver.Invoke(handlerType); - } - - if (handler == null) - { - handler = Activator.CreateInstance(handlerType); - } + object handler = DependencyResolver.ForceResolve(handlerType); return (TRegistrable)handler; } @@ -60,28 +50,7 @@ internal TRegistrable GetHandler(Type type, params object[] args) if (handlerType == null) return null; - object handler = null; - - if (Registrar.Resolver != null) - { - handler = Registrar.Resolver.Invoke(handlerType, args); - } - - if (handler != null) - { - return (TRegistrable)handler; - } - - // This is by no means a general solution to matching with the correct constructor, but it'll - // do for finding Android renderers which need Context (vs older custom renderers which may still use - // parameterless constructors) - if (handlerType.GetTypeInfo().DeclaredConstructors.Any(info => info.GetParameters().Length == args.Length)) - { - handler = Activator.CreateInstance(handlerType, args); - return (TRegistrable)handler; - } - - return GetHandler(type); + return (TRegistrable)DependencyResolver.ForceResolve(handlerType, args); } public TOut GetHandler(Type type) where TOut : TRegistrable @@ -252,27 +221,5 @@ public static void RegisterAll(Type[] attrTypes) DependencyService.Initialize(assemblies); } - - public delegate object ResolveDelegate(Type type, params object[] args); - - internal static ResolveDelegate Resolver { get; set; } - - public static void ResolveUsing(ResolveDelegate resolveDelegate) - { - Resolver = resolveDelegate; - } - - public static void ResolveUsing(Func resolveFunc) - { - object ResolveDelegate(Type type, object[] args) => resolveFunc.Invoke(type); - Resolver = ResolveDelegate; - } - - // Used for testing - internal static void Reset() - { - Registered = new Registrar(); - Resolver = null; - } } } From 76391ff45eca96a9abfc2049b20165d39b933c0a Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Tue, 13 Feb 2018 12:33:49 -0700 Subject: [PATCH 4/6] Update docs to reflect new changes --- ...=> DependencyResolver+ResolveDelegate.xml} | 6 +- .../DependencyResolver.xml | 56 +++++++++++++++++++ .../Xamarin.Forms.Internals/Registrar.xml | 38 ------------- docs/Xamarin.Forms.Core/index.xml | 3 +- 4 files changed, 61 insertions(+), 42 deletions(-) rename docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/{Registrar+ResolveDelegate.xml => DependencyResolver+ResolveDelegate.xml} (71%) create mode 100644 docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver.xml diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar+ResolveDelegate.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver+ResolveDelegate.xml similarity index 71% rename from docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar+ResolveDelegate.xml rename to docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver+ResolveDelegate.xml index 1f401578b3f..a3771d0266b 100644 --- a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar+ResolveDelegate.xml +++ b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver+ResolveDelegate.xml @@ -1,6 +1,6 @@ - - - + + + Xamarin.Forms.Core 2.0.0.0 diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver.xml new file mode 100644 index 00000000000..700d694f162 --- /dev/null +++ b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver.xml @@ -0,0 +1,56 @@ + + + + + Xamarin.Forms.Core + 2.0.0.0 + + + System.Object + + + + To be added. + To be added. + + + + + + Method + + 2.0.0.0 + + + System.Void + + + + + + To be added. + To be added. + To be added. + + + + + + Method + + 2.0.0.0 + + + System.Void + + + + + + To be added. + To be added. + To be added. + + + + diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar.xml index 48dee9ceea6..2968e885047 100644 --- a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar.xml +++ b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/Registrar.xml @@ -70,43 +70,5 @@ To be added. - - - - Method - - 2.0.0.0 - - - System.Void - - - - - - To be added. - To be added. - To be added. - - - - - - Method - - 2.0.0.0 - - - System.Void - - - - - - To be added. - To be added. - To be added. - - diff --git a/docs/Xamarin.Forms.Core/index.xml b/docs/Xamarin.Forms.Core/index.xml index 069a33d7b38..23669a6c11c 100644 --- a/docs/Xamarin.Forms.Core/index.xml +++ b/docs/Xamarin.Forms.Core/index.xml @@ -463,6 +463,8 @@ + + @@ -502,7 +504,6 @@ - From 3d42cf89bfb162c493fe5359996f7bca213f236d Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Fri, 16 Feb 2018 17:20:57 -0700 Subject: [PATCH 5/6] Add type checks closer to resolution; rename ForceResolve; remove public delegate type; --- .../DependencyResolutionTests.cs | 22 +++++++++++++-- Xamarin.Forms.Core/DependencyResolver.cs | 27 ++++++++++++------- Xamarin.Forms.Core/Effect.cs | 2 +- Xamarin.Forms.Core/Registrar.cs | 4 +-- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs b/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs index 6df9673beb7..017d5d53773 100644 --- a/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs +++ b/Xamarin.Forms.Core.UnitTests/DependencyResolutionTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; +using Xamarin.Forms.Internals; namespace Xamarin.Forms.Core.UnitTests { @@ -42,6 +43,14 @@ class MockServiceImpl : IMockService { } // (Just testing the type registration, don't need an instance) class MockServiceImpl2 : IMockService { } + class TypeWhichWillNotResolveCorrectly + { + } + + class IncorrectType + { + } + class MockContainer { readonly Dictionary _services; @@ -85,10 +94,19 @@ public object Resolve(Type type, params object[] args) public void SetUp() { _container = new MockContainer(); + object Resolver(Type type, object[] args) => _container.Resolve(type, args); - object ResolveDelegate(Type type, object[] args) => _container.Resolve(type, args); + DependencyResolver.ResolveUsing(Resolver); + } + + [Test] + public void ThrowsIfResolverReturnsWrongType() + { + _container = new MockContainer(); + _container.Register(typeof(TypeWhichWillNotResolveCorrectly), new IncorrectType()); + DependencyService.Register(); - Internals.DependencyResolver.ResolveUsing(ResolveDelegate); + Assert.Throws(() => DependencyService.Resolve()); } [Test] diff --git a/Xamarin.Forms.Core/DependencyResolver.cs b/Xamarin.Forms.Core/DependencyResolver.cs index 51e894a46fd..8eee49c1f0e 100644 --- a/Xamarin.Forms.Core/DependencyResolver.cs +++ b/Xamarin.Forms.Core/DependencyResolver.cs @@ -6,27 +6,34 @@ namespace Xamarin.Forms.Internals { public static class DependencyResolver { - public delegate object ResolveDelegate(Type type, params object[] args); + static Func Resolver { get; set; } - static ResolveDelegate Resolver { get; set; } - - public static void ResolveUsing(ResolveDelegate resolveDelegate) + public static void ResolveUsing(Func resolver) { - Resolver = resolveDelegate; + Resolver = resolver; } - public static void ResolveUsing(Func resolveFunc) + public static void ResolveUsing(Func resolver) { - object ResolveDelegate(Type type, object[] args) => resolveFunc.Invoke(type); - Resolver = ResolveDelegate; + Resolver = (type, objects) => resolver.Invoke(type); } internal static object Resolve(Type type, params object[] args) { - return Resolver?.Invoke(type, args); + var result = Resolver?.Invoke(type, args); + + if (result != null) + { + if (!type.IsInstanceOfType(result)) + { + throw new InvalidCastException("Resolved instance is not of the correct type."); + } + } + + return result; } - internal static object ForceResolve(Type type, params object[] args) + internal static object ResolveOrCreate(Type type, params object[] args) { var result = Resolve(type, args); diff --git a/Xamarin.Forms.Core/Effect.cs b/Xamarin.Forms.Core/Effect.cs index bf607a5d48f..63d81306303 100644 --- a/Xamarin.Forms.Core/Effect.cs +++ b/Xamarin.Forms.Core/Effect.cs @@ -23,7 +23,7 @@ public static Effect Resolve(string name) Effect result = null; if (Internals.Registrar.Effects.TryGetValue(name, out Type effectType)) { - result = DependencyResolver.ForceResolve(effectType) as Effect; + result = (Effect)DependencyResolver.ResolveOrCreate(effectType); } if (result == null) diff --git a/Xamarin.Forms.Core/Registrar.cs b/Xamarin.Forms.Core/Registrar.cs index d7b9490a151..f120e69a426 100644 --- a/Xamarin.Forms.Core/Registrar.cs +++ b/Xamarin.Forms.Core/Registrar.cs @@ -34,7 +34,7 @@ internal TRegistrable GetHandler(Type type) if (handlerType == null) return null; - object handler = DependencyResolver.ForceResolve(handlerType); + object handler = DependencyResolver.ResolveOrCreate(handlerType); return (TRegistrable)handler; } @@ -50,7 +50,7 @@ internal TRegistrable GetHandler(Type type, params object[] args) if (handlerType == null) return null; - return (TRegistrable)DependencyResolver.ForceResolve(handlerType, args); + return (TRegistrable)DependencyResolver.ResolveOrCreate(handlerType, args); } public TOut GetHandler(Type type) where TOut : TRegistrable From 30a8a3584ae5ee6bf3943a65e0cd2dc8d3e529fa Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Tue, 20 Feb 2018 17:59:03 -0700 Subject: [PATCH 6/6] Update docs --- .../DependencyResolver+ResolveDelegate.xml | 31 ------------------- .../DependencyResolver.xml | 16 +++++----- docs/Xamarin.Forms.Core/index.xml | 1 - 3 files changed, 8 insertions(+), 40 deletions(-) delete mode 100644 docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver+ResolveDelegate.xml diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver+ResolveDelegate.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver+ResolveDelegate.xml deleted file mode 100644 index a3771d0266b..00000000000 --- a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver+ResolveDelegate.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Xamarin.Forms.Core - 2.0.0.0 - - - System.Delegate - - - - - - - System.ParamArray - - - - - - System.Object - - - To be added. - To be added. - To be added. - To be added. - To be added. - - diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver.xml index 700d694f162..07584fcb4e7 100644 --- a/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver.xml +++ b/docs/Xamarin.Forms.Core/Xamarin.Forms.Internals/DependencyResolver.xml @@ -15,8 +15,8 @@ - - + + Method 2.0.0.0 @@ -25,17 +25,17 @@ System.Void - + - To be added. + To be added. To be added. To be added. - - + + Method 2.0.0.0 @@ -44,10 +44,10 @@ System.Void - + - To be added. + To be added. To be added. To be added. diff --git a/docs/Xamarin.Forms.Core/index.xml b/docs/Xamarin.Forms.Core/index.xml index 23669a6c11c..e23a3e69d37 100644 --- a/docs/Xamarin.Forms.Core/index.xml +++ b/docs/Xamarin.Forms.Core/index.xml @@ -464,7 +464,6 @@ -