From 0a0d4d5a8c4505505fe299aed451e2e81ec0badf Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 29 Jan 2024 17:02:14 +0900 Subject: [PATCH 1/4] Rework dependencies to allow value types --- .../Reflection/CachedAttributeTest.cs | 26 ++-- .../Reflection/DependencyContainerTest.cs | 112 +++++++-------- .../Reflection/ResolvedAttributeTest.cs | 38 ++++- .../SourceGeneration/CachedAttributeTest.cs | 20 +-- .../CachedModelDependenciesTest.cs | 22 ++- .../DependencyContainerTest.cs | 112 +++++++-------- .../SourceGeneration/ResolvedAttributeTest.cs | 40 +++++- .../CachedModelDependencyContainer.cs | 47 ++++-- .../Allocation/DependencyContainer.cs | 136 +++++++----------- .../IReadOnlyDependencyContainer.cs | 85 ++--------- .../Graphics/Containers/CompositeDrawable.cs | 2 +- osu.Framework/Utils/SourceGeneratorUtils.cs | 12 +- 12 files changed, 320 insertions(+), 332 deletions(-) diff --git a/osu.Framework.Tests/Dependencies/Reflection/CachedAttributeTest.cs b/osu.Framework.Tests/Dependencies/Reflection/CachedAttributeTest.cs index ee6de70e59..27817b34f9 100644 --- a/osu.Framework.Tests/Dependencies/Reflection/CachedAttributeTest.cs +++ b/osu.Framework.Tests/Dependencies/Reflection/CachedAttributeTest.cs @@ -51,11 +51,13 @@ public void TestCacheTypeOverrideParentCache() } [Test] - public void TestAttemptToCacheStruct() + public void TestCacheStruct() { var provider = new Provider4(); - Assert.Throws(() => DependencyActivator.MergeDependencies(provider, new DependencyContainer())); + var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); + + Assert.IsNotNull(dependencies.Get()); } [Test] @@ -136,7 +138,9 @@ public void TestCacheStructAsInterface() { var provider = new Provider12(); - Assert.Throws(() => DependencyActivator.MergeDependencies(provider, new DependencyContainer())); + var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); + + Assert.IsNotNull(dependencies.Get()); } /// @@ -149,13 +153,13 @@ public void TestCacheStructInternal() var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); - Assert.AreEqual(provider.CachedObject.Value, dependencies.GetValue().Value); + Assert.AreEqual(provider.CachedObject.Value, dependencies.Get().Value); } [Test] - public void TestGetValueNullInternal() + public void TestGetNullInternal() { - Assert.AreEqual(default(int), new DependencyContainer().GetValue()); + Assert.AreEqual(default(int), new DependencyContainer().Get()); } /// @@ -171,7 +175,7 @@ public void TestCacheNullableInternal(int? testValue) var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); - Assert.AreEqual(testValue, dependencies.GetValue()); + Assert.AreEqual(testValue, dependencies.Get()); } [Test] @@ -460,9 +464,7 @@ private class Provider22 : IDependencyInjectionCandidate public object Provided1 { get => null; - set - { - } + set { } } } @@ -471,9 +473,7 @@ private class Provider23 : IDependencyInjectionCandidate [Cached] public object Provided1 { - set - { - } + set { } } } diff --git a/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs b/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs index 80d3ffed1c..4bee79d3ed 100644 --- a/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs +++ b/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs @@ -6,7 +6,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading; -using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Testing.Dependencies; @@ -137,22 +136,22 @@ public void TestMultipleCacheFails() Assert.Throws(() => dependencies.Cache(testObject2)); } - /// - /// Special case because "where T : class" also allows interfaces. - /// [Test] - public void TestAttemptCacheStruct() + public void TestCacheStruct() { - Assert.Throws(() => new DependencyContainer().Cache(new BaseStructObject())); + var dependencies = new DependencyContainer(); + dependencies.Cache(new BaseStructObject()); + + Assert.IsNotNull(dependencies.Get()); } - /// - /// Special case because "where T : class" also allows interfaces. - /// [Test] - public void TestAttemptCacheAsStruct() + public void TestCacheAsStruct() { - Assert.Throws(() => new DependencyContainer().CacheAs(new BaseStructObject())); + var dependencies = new DependencyContainer(); + dependencies.CacheAs(new BaseStructObject()); + + Assert.IsNotNull(dependencies.Get()); } /// @@ -166,9 +165,9 @@ public void TestCacheCancellationToken() var dependencies = new DependencyContainer(); - Assert.DoesNotThrow(() => dependencies.CacheValue(token)); + Assert.DoesNotThrow(() => dependencies.Cache(token)); - var retrieved = dependencies.GetValue(); + var retrieved = dependencies.Get(); source.Cancel(); @@ -238,16 +237,19 @@ public void TestResolveNullableInternal(int? testValue) } [Test] - public void TestCacheNullInternal() + public void TestAttemptCacheNullInternal() { - Assert.DoesNotThrow(() => new DependencyContainer().CacheValue(null)); - Assert.DoesNotThrow(() => new DependencyContainer().CacheValueAs(null)); + Assert.Throws(() => new DependencyContainer().Cache(null!)); + Assert.Throws(() => new DependencyContainer().CacheAs(null!)); } [Test] public void TestResolveStructWithoutNullPermits() { - Assert.Throws(() => new DependencyContainer().Inject(new Receiver12())); + var receiver = new Receiver12(); + + Assert.DoesNotThrow(() => new DependencyContainer().Inject(receiver)); + Assert.AreEqual(0, receiver.TestObject); } [Test] @@ -265,60 +267,43 @@ public void TestCacheAsNullableInternal() int? testObject = 5; var dependencies = new DependencyContainer(); - dependencies.CacheValueAs(testObject); + dependencies.CacheAs(testObject); - Assert.AreEqual(testObject, dependencies.GetValue()); - Assert.AreEqual(testObject, dependencies.GetValue()); + Assert.AreEqual(testObject, dependencies.Get()); + Assert.AreEqual(testObject, dependencies.Get()); } - [Test] - public void TestCacheWithDependencyInfo() + [TestCase(null, null)] + [TestCase("name", null)] + [TestCase(null, typeof(object))] + [TestCase("name", typeof(object))] + public void TestCacheWithDependencyInfo(string name, Type parent) { - var cases = new[] - { - default, - new CacheInfo("name"), - new CacheInfo(parent: typeof(object)), - new CacheInfo("name", typeof(object)) - }; + CacheInfo info = new CacheInfo(name, parent); var dependencies = new DependencyContainer(); + dependencies.CacheAs(1, info); - for (int i = 0; i < cases.Length; i++) - dependencies.CacheValueAs(i, cases[i]); - - Assert.Multiple(() => - { - for (int i = 0; i < cases.Length; i++) - Assert.AreEqual(i, dependencies.GetValue(cases[i])); - }); + Assert.AreEqual(1, dependencies.Get(info)); } - [Test] - public void TestDependenciesOverrideParent() + [TestCase(null, null)] + [TestCase("name", null)] + [TestCase(null, typeof(object))] + [TestCase("name", typeof(object))] + public void TestDependenciesOverrideParent(string name, Type parent) { - var cases = new[] - { - default, - new CacheInfo("name"), - new CacheInfo(parent: typeof(object)), - new CacheInfo("name", typeof(object)) - }; + CacheInfo info = new CacheInfo(name, parent); var dependencies = new DependencyContainer(); - - for (int i = 0; i < cases.Length; i++) - dependencies.CacheValueAs(i, cases[i]); + dependencies.CacheAs(1, info); dependencies = new DependencyContainer(dependencies); - - for (int i = 0; i < cases.Length; i++) - dependencies.CacheValueAs(cases.Length + i, cases[i]); + dependencies.CacheAs(2, info); Assert.Multiple(() => { - for (int i = 0; i < cases.Length; i++) - Assert.AreEqual(cases.Length + i, dependencies.GetValue(cases[i])); + Assert.AreEqual(2, dependencies.Get(info)); }); } @@ -336,6 +321,18 @@ public void TestResolveWithNullableReferenceTypes() Assert.DoesNotThrow(() => dependencies.Inject(receiver)); } + [Test] + public void TestResolveDefaultStruct() + { + Assert.That(new DependencyContainer().Get(), Is.EqualTo(default(CancellationToken))); + } + + [Test] + public void TestResolveNullStruct() + { + Assert.That(new DependencyContainer().Get(), Is.Null); + } + private interface IBaseInterface { } @@ -439,11 +436,10 @@ private class Receiver11 : IDependencyInjectionCandidate private class Receiver12 : IDependencyInjectionCandidate { - [UsedImplicitly] // param used implicitly + public int TestObject { get; private set; } = 1; + [BackgroundDependencyLoader] - private void load(int testObject) - { - } + private void load(int testObject) => TestObject = testObject; } private class Receiver13 : IDependencyInjectionCandidate diff --git a/osu.Framework.Tests/Dependencies/Reflection/ResolvedAttributeTest.cs b/osu.Framework.Tests/Dependencies/Reflection/ResolvedAttributeTest.cs index e46fd5bd60..abc9e33c13 100644 --- a/osu.Framework.Tests/Dependencies/Reflection/ResolvedAttributeTest.cs +++ b/osu.Framework.Tests/Dependencies/Reflection/ResolvedAttributeTest.cs @@ -4,6 +4,7 @@ #nullable disable using System.Diagnostics.CodeAnalysis; +using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -160,7 +161,10 @@ public void TestResolveNullableInternal(int? testValue) [Test] public void TestResolveStructWithoutNullPermits() { - Assert.Throws(() => new DependencyContainer().Inject(new Receiver14())); + var receiver = new Receiver14(); + + Assert.DoesNotThrow(() => new DependencyContainer().Inject(receiver)); + Assert.AreEqual(0, receiver.Obj); } [Test] @@ -212,6 +216,26 @@ public void TestResolveNonNullWithNullableReferenceTypes() Assert.DoesNotThrow(() => createDependencies(new Bindable(10)).Inject(receiver)); } + [Test] + public void TestResolveDefaultStruct() + { + var receiver = new Receiver19(); + + createDependencies().Inject(receiver); + + Assert.That(receiver.Token, Is.EqualTo(default(CancellationToken))); + } + + [Test] + public void TestResolveNullStruct() + { + var receiver = new Receiver20(); + + createDependencies().Inject(receiver); + + Assert.That(receiver.Token, Is.Null); + } + private DependencyContainer createDependencies(params object[] toCache) { var dependencies = new DependencyContainer(); @@ -342,5 +366,17 @@ private class Receiver18 : IDependencyInjectionCandidate [Resolved] public Bindable Obj { get; private set; } = null!; } + + private class Receiver19 : IDependencyInjectionCandidate + { + [Resolved] + public CancellationToken Token { get; private set; } + } + + private class Receiver20 : IDependencyInjectionCandidate + { + [Resolved] + public CancellationToken? Token { get; private set; } + } } } diff --git a/osu.Framework.Tests/Dependencies/SourceGeneration/CachedAttributeTest.cs b/osu.Framework.Tests/Dependencies/SourceGeneration/CachedAttributeTest.cs index ac928cc1e9..29fea93bc4 100644 --- a/osu.Framework.Tests/Dependencies/SourceGeneration/CachedAttributeTest.cs +++ b/osu.Framework.Tests/Dependencies/SourceGeneration/CachedAttributeTest.cs @@ -48,11 +48,13 @@ public void TestCacheTypeOverrideParentCache() } [Test] - public void TestAttemptToCacheStruct() + public void TestCacheStruct() { var provider = new Provider4(); - Assert.Throws(() => DependencyActivator.MergeDependencies(provider, new DependencyContainer())); + var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); + + Assert.IsNotNull(dependencies.Get()); } [Test] @@ -133,7 +135,9 @@ public void TestCacheStructAsInterface() { var provider = new Provider12(); - Assert.Throws(() => DependencyActivator.MergeDependencies(provider, new DependencyContainer())); + var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); + + Assert.IsNotNull(dependencies.Get()); } /// @@ -146,13 +150,13 @@ public void TestCacheStructInternal() var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); - Assert.AreEqual(provider.CachedObject.Value, dependencies.GetValue().Value); + Assert.AreEqual(provider.CachedObject.Value, dependencies.Get().Value); } [Test] public void TestGetValueNullInternal() { - Assert.AreEqual(default(int), new DependencyContainer().GetValue()); + Assert.AreEqual(default(int), new DependencyContainer().Get()); } /// @@ -168,7 +172,7 @@ public void TestCacheNullableInternal(int? testValue) var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); - Assert.AreEqual(testValue, dependencies.GetValue()); + Assert.AreEqual(testValue, dependencies.Get()); } [Test] @@ -457,9 +461,7 @@ public object Provided1 { get => null; // ReSharper disable once ValueParameterNotUsed - set - { - } + set { } } } diff --git a/osu.Framework.Tests/Dependencies/SourceGeneration/CachedModelDependenciesTest.cs b/osu.Framework.Tests/Dependencies/SourceGeneration/CachedModelDependenciesTest.cs index 8555380a72..c6b0391f01 100644 --- a/osu.Framework.Tests/Dependencies/SourceGeneration/CachedModelDependenciesTest.cs +++ b/osu.Framework.Tests/Dependencies/SourceGeneration/CachedModelDependenciesTest.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using NUnit.Framework; using osu.Framework.Allocation; @@ -83,7 +81,7 @@ public void TestChangeModelValuePropagatesToChildren() Assert.AreEqual(2, resolver.Model.Bindable.Value); - dependencies.Model.Value.Bindable.Value = 3; + dependencies.Model.Value!.Bindable.Value = 3; Assert.AreEqual(3, resolver.Model.Bindable.Value); } @@ -109,7 +107,7 @@ public void TestSubClassedModelCachedAllSuperClasses() Assert.AreEqual(2, resolver.Model.Bindable.Value); Assert.AreEqual("2", resolver.Model.BindableString.Value); - dependencies.Model.Value.Bindable.Value = 3; + dependencies.Model.Value!.Bindable.Value = 3; dependencies.Model.Value.BindableString.Value = "3"; Assert.AreEqual(3, resolver.Model.Bindable.Value); @@ -283,15 +281,15 @@ private partial class NonBindablePrivateFieldModel : IDependencyInjectionCandida private partial class NonReadOnlyFieldModel : IDependencyInjectionCandidate { -#pragma warning disable 649 - public Bindable Bindable; -#pragma warning restore 649 +#pragma warning disable CS0414 // Field is assigned but its value is never used + public Bindable Bindable = null!; +#pragma warning restore CS0414 // Field is assigned but its value is never used } private partial class PropertyModel : IDependencyInjectionCandidate { // ReSharper disable once UnusedMember.Local - public Bindable Bindable { get; private set; } + public Bindable Bindable { get; private set; } = null!; } private partial class FieldModel : IDependencyInjectionCandidate @@ -309,22 +307,22 @@ private partial class DerivedFieldModel : FieldModel private partial class FieldModelResolver : IDependencyInjectionCandidate { [Resolved] - public FieldModel Model { get; private set; } + public FieldModel Model { get; private set; } = null!; } private partial class DerivedFieldModelResolver : IDependencyInjectionCandidate { [Resolved] - public DerivedFieldModel Model { get; private set; } + public DerivedFieldModel Model { get; private set; } = null!; } private partial class DerivedFieldModelPropertyResolver : IDependencyInjectionCandidate { [Resolved(typeof(DerivedFieldModel))] - public Bindable Bindable { get; private set; } + public Bindable Bindable { get; private set; } = null!; [Resolved(typeof(DerivedFieldModel))] - public Bindable BindableString { get; private set; } + public Bindable BindableString { get; private set; } = null!; } } } diff --git a/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs b/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs index 0285c41a86..9aaa9355e5 100644 --- a/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs +++ b/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs @@ -6,7 +6,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading; -using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Testing.Dependencies; @@ -136,22 +135,22 @@ public void TestMultipleCacheFails() Assert.Throws(() => dependencies.Cache(testObject2)); } - /// - /// Special case because "where T : class" also allows interfaces. - /// [Test] - public void TestAttemptCacheStruct() + public void TestCacheStruct() { - Assert.Throws(() => new DependencyContainer().Cache(new BaseStructObject())); + var dependencies = new DependencyContainer(); + dependencies.Cache(new BaseStructObject()); + + Assert.IsNotNull(dependencies.Get()); } - /// - /// Special case because "where T : class" also allows interfaces. - /// [Test] - public void TestAttemptCacheAsStruct() + public void TestCacheAsStruct() { - Assert.Throws(() => new DependencyContainer().CacheAs(new BaseStructObject())); + var dependencies = new DependencyContainer(); + dependencies.CacheAs(new BaseStructObject()); + + Assert.IsNotNull(dependencies.Get()); } /// @@ -165,9 +164,9 @@ public void TestCacheCancellationToken() var dependencies = new DependencyContainer(); - Assert.DoesNotThrow(() => dependencies.CacheValue(token)); + Assert.DoesNotThrow(() => dependencies.Cache(token)); - var retrieved = dependencies.GetValue(); + var retrieved = dependencies.Get(); source.Cancel(); @@ -241,16 +240,19 @@ public void TestResolveNullableInternal(int? testValue) } [Test] - public void TestCacheNullInternal() + public void TestAttemptCacheNullInternal() { - Assert.DoesNotThrow(() => new DependencyContainer().CacheValue(null)); - Assert.DoesNotThrow(() => new DependencyContainer().CacheValueAs(null)); + Assert.Throws(() => new DependencyContainer().Cache(null!)); + Assert.Throws(() => new DependencyContainer().CacheAs(null!)); } [Test] public void TestResolveStructWithoutNullPermits() { - Assert.Throws(() => new DependencyContainer().Inject(new Receiver12())); + var receiver = new Receiver12(); + + Assert.DoesNotThrow(() => new DependencyContainer().Inject(receiver)); + Assert.AreEqual(0, receiver.TestObject); } [Test] @@ -268,60 +270,43 @@ public void TestCacheAsNullableInternal() int? testObject = 5; var dependencies = new DependencyContainer(); - dependencies.CacheValueAs(testObject); + dependencies.CacheAs(testObject); - Assert.AreEqual(testObject, dependencies.GetValue()); - Assert.AreEqual(testObject, dependencies.GetValue()); + Assert.AreEqual(testObject, dependencies.Get()); + Assert.AreEqual(testObject, dependencies.Get()); } - [Test] - public void TestCacheWithDependencyInfo() + [TestCase(null, null)] + [TestCase("name", null)] + [TestCase(null, typeof(object))] + [TestCase("name", typeof(object))] + public void TestCacheWithDependencyInfo(string name, Type parent) { - var cases = new[] - { - default, - new CacheInfo("name"), - new CacheInfo(parent: typeof(object)), - new CacheInfo("name", typeof(object)) - }; + CacheInfo info = new CacheInfo(name, parent); var dependencies = new DependencyContainer(); + dependencies.CacheAs(1, info); - for (int i = 0; i < cases.Length; i++) - dependencies.CacheValueAs(i, cases[i]); - - Assert.Multiple(() => - { - for (int i = 0; i < cases.Length; i++) - Assert.AreEqual(i, dependencies.GetValue(cases[i])); - }); + Assert.AreEqual(1, dependencies.Get(info)); } - [Test] - public void TestDependenciesOverrideParent() + [TestCase(null, null)] + [TestCase("name", null)] + [TestCase(null, typeof(object))] + [TestCase("name", typeof(object))] + public void TestDependenciesOverrideParent(string name, Type parent) { - var cases = new[] - { - default, - new CacheInfo("name"), - new CacheInfo(parent: typeof(object)), - new CacheInfo("name", typeof(object)) - }; + CacheInfo info = new CacheInfo(name, parent); var dependencies = new DependencyContainer(); - - for (int i = 0; i < cases.Length; i++) - dependencies.CacheValueAs(i, cases[i]); + dependencies.CacheAs(1, info); dependencies = new DependencyContainer(dependencies); - - for (int i = 0; i < cases.Length; i++) - dependencies.CacheValueAs(cases.Length + i, cases[i]); + dependencies.CacheAs(2, info); Assert.Multiple(() => { - for (int i = 0; i < cases.Length; i++) - Assert.AreEqual(cases.Length + i, dependencies.GetValue(cases[i])); + Assert.AreEqual(2, dependencies.Get(info)); }); } @@ -339,6 +324,18 @@ public void TestResolveWithNullableReferenceTypes() Assert.DoesNotThrow(() => dependencies.Inject(receiver)); } + [Test] + public void TestResolveDefaultStruct() + { + Assert.That(new DependencyContainer().Get(), Is.EqualTo(default(CancellationToken))); + } + + [Test] + public void TestResolveNullStruct() + { + Assert.That(new DependencyContainer().Get(), Is.Null); + } + private interface IBaseInterface { } @@ -443,11 +440,10 @@ private partial class Receiver11 : IDependencyInjectionCandidate private partial class Receiver12 : IDependencyInjectionCandidate { - [UsedImplicitly] // param used implicitly + public int TestObject { get; private set; } = 1; + [BackgroundDependencyLoader] - private void load(int testObject) - { - } + private void load(int testObject) => TestObject = testObject; } private partial class Receiver13 : IDependencyInjectionCandidate diff --git a/osu.Framework.Tests/Dependencies/SourceGeneration/ResolvedAttributeTest.cs b/osu.Framework.Tests/Dependencies/SourceGeneration/ResolvedAttributeTest.cs index fa15a2d1d2..8f90fa91e9 100644 --- a/osu.Framework.Tests/Dependencies/SourceGeneration/ResolvedAttributeTest.cs +++ b/osu.Framework.Tests/Dependencies/SourceGeneration/ResolvedAttributeTest.cs @@ -3,6 +3,7 @@ #nullable disable +using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -157,7 +158,10 @@ public void TestResolveNullableInternal(int? testValue) [Test] public void TestResolveStructWithoutNullPermits() { - Assert.Throws(() => new DependencyContainer().Inject(new Receiver14())); + var receiver = new Receiver14(); + + Assert.DoesNotThrow(() => new DependencyContainer().Inject(receiver)); + Assert.AreEqual(0, receiver.Obj); } [Test] @@ -209,6 +213,26 @@ public void TestResolveNonNullWithNullableReferenceTypes() Assert.DoesNotThrow(() => createDependencies(new Bindable(10)).Inject(receiver)); } + [Test] + public void TestResolveDefaultStruct() + { + var receiver = new Receiver19(); + + createDependencies().Inject(receiver); + + Assert.That(receiver.Token, Is.EqualTo(default(CancellationToken))); + } + + [Test] + public void TestResolveNullStruct() + { + var receiver = new Receiver20(); + + createDependencies().Inject(receiver); + + Assert.That(receiver.Token, Is.Null); + } + private DependencyContainer createDependencies(params object[] toCache) { var dependencies = new DependencyContainer(); @@ -303,7 +327,7 @@ private partial class Receiver13 : IDependencyInjectionCandidate private partial class Receiver14 : IDependencyInjectionCandidate { [Resolved] - public int Obj { get; private set; } + public int Obj { get; private set; } = 1; } private partial class Receiver15 : IDependencyInjectionCandidate @@ -333,5 +357,17 @@ private partial class Receiver18 : IDependencyInjectionCandidate [Resolved] public Bindable Obj { get; private set; } = null!; } + + private partial class Receiver19 : IDependencyInjectionCandidate + { + [Resolved] + public CancellationToken Token { get; private set; } + } + + private partial class Receiver20 : IDependencyInjectionCandidate + { + [Resolved] + public CancellationToken? Token { get; private set; } + } } } diff --git a/osu.Framework/Allocation/CachedModelDependencyContainer.cs b/osu.Framework/Allocation/CachedModelDependencyContainer.cs index ec3f0768f1..c44849bc05 100644 --- a/osu.Framework/Allocation/CachedModelDependencyContainer.cs +++ b/osu.Framework/Allocation/CachedModelDependencyContainer.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Reflection; using osu.Framework.Bindables; @@ -29,20 +27,20 @@ public class CachedModelDependencyContainer : IReadOnlyDependencyContain /// /// This model is not injected directly, users of the receive a shadow-bound copy of this value in all cases. /// - public readonly Bindable Model = new Bindable(); + public readonly Bindable Model = new Bindable(); private readonly TModel shadowModel = new TModel(); - private readonly IReadOnlyDependencyContainer parent; + private readonly IReadOnlyDependencyContainer? parent; private readonly IReadOnlyDependencyContainer shadowDependencies; - public CachedModelDependencyContainer(IReadOnlyDependencyContainer parent) + public CachedModelDependencyContainer(IReadOnlyDependencyContainer? parent) { this.parent = parent; shadowDependencies = DependencyActivator.MergeDependencies(shadowModel, null, new CacheInfo(parent: typeof(TModel))); - TModel currentModel = null; + TModel? currentModel = null; Model.BindValueChanged(e => { // When setting a null model, we actually want to reset the shadow model to a default state @@ -55,9 +53,34 @@ public CachedModelDependencyContainer(IReadOnlyDependencyContainer parent) }); } - public object Get(Type type) => Get(type, default); + public T Get() => Get(default); + + public T Get(CacheInfo info) + { + TryGet(out T value, info); + return value; + } + + public bool TryGet(out T value) => TryGet(out value, default); + + public bool TryGet(out T value, CacheInfo info) + { + object? obj = Get(typeof(T), info); + + if (obj == null) + { + // `(int)(object)null` throws a NRE, so `default` is used instead. + value = default!; + return false; + } + + value = (T)obj; + return true; + } + + public object? Get(Type type) => Get(type, default); - public object Get(Type type, CacheInfo info) + public object? Get(Type type, CacheInfo info) { if (info.Parent == null) return type == typeof(TModel) ? createChildShadowModel() : parent?.Get(type, info); @@ -87,7 +110,7 @@ private TModel createChildShadowModel() /// The shadow model to update. /// The model to unbind from. /// The model to bind to. - private void updateShadowModel(TModel targetShadowModel, TModel lastModel, TModel newModel) + private void updateShadowModel(TModel targetShadowModel, TModel? lastModel, TModel newModel) { // Due to static-constructor checks, we are guaranteed that all fields will be IBindable @@ -111,18 +134,18 @@ private void updateShadowModel(TModel targetShadowModel, TModel lastModel, TMode /// /// Perform an arbitrary action across a shadow model and model. /// - private void perform(TModel targetShadowModel, MemberInfo member, TModel target, Action action) + private void perform(TModel targetShadowModel, MemberInfo member, TModel? target, Action action) { if (target == null) return; switch (member) { case PropertyInfo pi: - action((IBindable)pi.GetValue(targetShadowModel), (IBindable)pi.GetValue(target)); + action((IBindable)pi.GetValue(targetShadowModel)!, (IBindable)pi.GetValue(target)!); break; case FieldInfo fi: - action((IBindable)fi.GetValue(targetShadowModel), (IBindable)fi.GetValue(target)); + action((IBindable)fi.GetValue(targetShadowModel)!, (IBindable)fi.GetValue(target)!); break; } } diff --git a/osu.Framework/Allocation/DependencyContainer.cs b/osu.Framework/Allocation/DependencyContainer.cs index 897f663d0d..b05c62576c 100644 --- a/osu.Framework/Allocation/DependencyContainer.cs +++ b/osu.Framework/Allocation/DependencyContainer.cs @@ -1,11 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; -using System.Threading; using osu.Framework.Extensions.TypeExtensions; namespace osu.Framework.Allocation @@ -17,142 +14,85 @@ public class DependencyContainer : IReadOnlyDependencyContainer { private readonly Dictionary cache = new Dictionary(); - private readonly IReadOnlyDependencyContainer parentContainer; + private readonly IReadOnlyDependencyContainer? parentContainer; /// /// Create a new DependencyContainer instance. /// /// An optional parent container which we should use as a fallback for cache lookups. - public DependencyContainer(IReadOnlyDependencyContainer parent = null) + public DependencyContainer(IReadOnlyDependencyContainer? parent = null) { parentContainer = parent; } /// - /// Caches an instance of a type as its most derived type. This instance will be returned each time you . + /// Caches an instance of a type as its most derived type. This instance will be returned each time you . /// /// The instance to cache. public void Cache(object instance) => Cache(instance, default); /// - /// Caches an instance of a type as its most derived type. This instance will be returned each time you . + /// Caches an instance of a type as its most derived type. This instance will be returned each time you . /// /// The instance to cache. /// Extra information to identify in the cache. public void Cache(object instance, CacheInfo info) - => CacheAs(instance.GetType(), info, instance, false); + { + if (instance == null) + throw new ArgumentNullException(nameof(instance)); + + CacheAs(instance.GetType(), info, instance); + } /// - /// Caches an instance of a type as a type of . This instance will be returned each time you . + /// Caches an instance of a type as a type of . This instance will be returned each time you . /// /// The instance to cache. Must be or derive from . - public void CacheAs(T instance) where T : class + public void CacheAs(T instance) => CacheAs(instance, default); /// - /// Caches an instance of a type as a type of . This instance will be returned each time you . + /// Caches an instance of a type as a type of . This instance will be returned each time you . /// /// The instance to cache. Must be or derive from . /// Extra information to identify in the cache. - public void CacheAs(T instance, CacheInfo info) where T : class - => CacheAs(typeof(T), info, instance, false); + public void CacheAs(T instance, CacheInfo info) + => CacheAs(typeof(T), info, instance); /// - /// Caches an instance of a type as a type of . This instance will be returned each time you . + /// Caches an instance of a type as a type of . This instance will be returned each time you . /// /// The type to cache as. /// The instance to cache. Must be or derive from . - public void CacheAs(Type type, T instance) where T : class + public void CacheAs(Type type, T instance) => CacheAs(type, instance, default); /// - /// Caches an instance of a type as a type of . This instance will be returned each time you . + /// Caches an instance of a type as a type of . This instance will be returned each time you . /// /// The type to cache as. /// The instance to cache. Must be or derive from . /// Extra information to identify in the cache. - public void CacheAs(Type type, T instance, CacheInfo info) where T : class - => CacheAs(type, info, instance, false); - - /// - /// Caches an instance of a type as its most derived type. This instance will be returned each time you . - /// - /// - /// This should only be used when it is guaranteed that the internal state of the type will remain consistent through retrieval. - /// (e.g. or reference types). - /// - /// The instance to cache. - internal void CacheValue(object instance) - => CacheValue(instance, default); - - /// - /// Caches an instance of a type as its most derived type. This instance will be returned each time you . - /// - /// - /// This should only be used when it is guaranteed that the internal state of the type will remain consistent through retrieval. - /// (e.g. or reference types). - /// - /// The instance to cache. - /// Extra information to identify in the cache. - internal void CacheValue(object instance, CacheInfo info) - { - if (instance == null) - return; - - CacheAs(instance.GetType(), info, instance, true); - } - - /// - /// Caches an instance of a type as a type of . This instance will be returned each time you . - /// - /// - /// This should only be used when it is guaranteed that the internal state of the type will remain consistent through retrieval. - /// (e.g. or reference types). - /// - /// The instance to cache. Must be or derive from . - internal void CacheValueAs(T instance) - => CacheValueAs(instance, default); - - /// - /// Caches an instance of a type as a type of . This instance will be returned each time you . - /// - /// - /// This should only be used when it is guaranteed that the internal state of the type will remain consistent through retrieval. - /// (e.g. or reference types). - /// - /// The instance to cache. Must be or derive from . - /// Extra information to identify in the cache. - internal void CacheValueAs(T instance, CacheInfo info) - => CacheAs(typeof(T), info, instance, true); + public void CacheAs(Type type, T instance, CacheInfo info) + => CacheAs(type, info, instance); /// - /// Caches an instance of a type as a type of . This instance will be returned each time you . + /// Caches an instance of a type as a type of . This instance will be returned each time you . /// /// The type to cache as. /// Extra information to identify in the cache. /// The instance to cache. Must be or derive from . - /// Whether value types are allowed to be cached. - /// This should only be used when it is guaranteed that the internal state of the type will remain consistent through retrieval. - /// (e.g. or reference types). - internal void CacheAs(Type type, CacheInfo info, object instance, bool allowValueTypes) + internal void CacheAs(Type type, CacheInfo info, object? instance) { if (instance == null) - { - if (allowValueTypes) - return; - throw new ArgumentNullException(nameof(instance)); - } info = info.WithType(type.GetUnderlyingNullableType() ?? type); var instanceType = instance.GetType(); instanceType = instanceType.GetUnderlyingNullableType() ?? instanceType; - if (instanceType.IsValueType && !allowValueTypes) - throw new ArgumentException($"{instanceType.ReadableName()} must be a class to be cached as a dependency.", nameof(instance)); - if (!info.Type.IsInstanceOfType(instance)) throw new ArgumentException($"{instanceType.ReadableName()} must be a subclass of {info.Type.ReadableName()}.", nameof(instance)); @@ -164,14 +104,38 @@ internal void CacheAs(Type type, CacheInfo info, object instance, bool allowValu cache[info] = instance; } - public object Get(Type type) - => Get(type, default); + public T Get() => Get(default); + + public T Get(CacheInfo info) + { + TryGet(out T value, info); + return value; + } + + public bool TryGet(out T value) => TryGet(out value, default); + + public bool TryGet(out T value, CacheInfo info) + { + object? obj = Get(typeof(T), info); + + if (obj == null) + { + // `(int)(object)null` throws a NRE, so `default` is used instead. + value = default!; + return false; + } + + value = (T)obj; + return true; + } + + public object? Get(Type type) => Get(type, default); - public object Get(Type type, CacheInfo info) + public object? Get(Type type, CacheInfo info) { info = info.WithType(type.GetUnderlyingNullableType() ?? type); - if (cache.TryGetValue(info, out object existing)) + if (cache.TryGetValue(info, out object? existing)) return existing; return parentContainer?.Get(type, info); diff --git a/osu.Framework/Allocation/IReadOnlyDependencyContainer.cs b/osu.Framework/Allocation/IReadOnlyDependencyContainer.cs index 3189e7e7f9..bb8051a9fa 100644 --- a/osu.Framework/Allocation/IReadOnlyDependencyContainer.cs +++ b/osu.Framework/Allocation/IReadOnlyDependencyContainer.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; namespace osu.Framework.Allocation @@ -12,101 +10,48 @@ namespace osu.Framework.Allocation /// on types. /// public interface IReadOnlyDependencyContainer - { - /// - /// Retrieves a cached dependency of if it exists and null otherwise. - /// - /// The dependency type to query for. - /// The requested dependency, or null if not found. - object Get(Type type); - - /// - /// Retrieves a cached dependency of if it exists and null otherwise. - /// - /// The dependency type to query for. - /// Extra information that identifies the cached dependency. - /// The requested dependency, or null if not found. - object Get(Type type, CacheInfo info); - - /// - /// Injects dependencies into the given instance. - /// - /// The type of the instance to inject dependencies into. - /// The instance to inject dependencies into. - void Inject(T instance) where T : class, IDependencyInjectionCandidate; - } - - public static class ReadOnlyDependencyContainerExtensions { /// /// Retrieves a cached dependency of type if it exists, and null otherwise. /// /// The dependency type to query for. - /// The to query. /// The requested dependency, or null if not found. - public static T Get(this IReadOnlyDependencyContainer container) - where T : class - => Get(container, default); + T Get(); /// /// Retrieves a cached dependency of type if it exists, and null otherwise. /// /// The dependency type to query for. - /// The to query. /// Extra information that identifies the cached dependency. /// The requested dependency, or null if not found. - public static T Get(this IReadOnlyDependencyContainer container, CacheInfo info) - where T : class - => (T)container.Get(typeof(T), info); - - /// - /// Retrieves a cached dependency of type if it exists, and default() otherwise. - /// - /// The dependency type to query for. - /// The to query. - /// The requested dependency, or default() if not found. - internal static T GetValue(this IReadOnlyDependencyContainer container) - => GetValue(container, default); - - /// - /// Retrieves a cached dependency of type if it exists, and default() otherwise. - /// - /// The dependency type to query for. - /// The to query. - /// Extra information that identifies the cached dependency. - /// The requested dependency, or default() if not found. - internal static T GetValue(this IReadOnlyDependencyContainer container, CacheInfo info) - { - if (container.Get(typeof(T), info) is T value) - return value; - - return default; - } + T Get(CacheInfo info); /// /// Tries to retrieve a cached dependency of type . /// - /// The to query. /// The requested dependency, or null if not found. /// The dependency type to query for. /// Whether the requested dependency existed. - public static bool TryGet(this IReadOnlyDependencyContainer container, out T value) - where T : class - => TryGet(container, out value, default); + bool TryGet(out T value); /// /// Tries to retrieve a cached dependency of type . /// - /// The to query. /// The requested dependency, or null if not found. /// Extra information that identifies the cached dependency. /// The dependency type to query for. /// Whether the requested dependency existed. - public static bool TryGet(this IReadOnlyDependencyContainer container, out T value, CacheInfo info) - where T : class - { - value = container.Get(info); - return value != null; - } + bool TryGet(out T value, CacheInfo info); + + object? Get(Type type); + + object? Get(Type type, CacheInfo info); + + /// + /// Injects dependencies into the given instance. + /// + /// The type of the instance to inject dependencies into. + /// The instance to inject dependencies into. + void Inject(T instance) where T : class, IDependencyInjectionCandidate; } } diff --git a/osu.Framework/Graphics/Containers/CompositeDrawable.cs b/osu.Framework/Graphics/Containers/CompositeDrawable.cs index 615533bc25..d80c4d9cff 100644 --- a/osu.Framework/Graphics/Containers/CompositeDrawable.cs +++ b/osu.Framework/Graphics/Containers/CompositeDrawable.cs @@ -153,7 +153,7 @@ protected internal Task LoadComponentsAsync(IEnumerable co var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(disposalCancellationSource.Token, cancellation); var deps = new DependencyContainer(Dependencies); - deps.CacheValueAs(linkedSource.Token); + deps.CacheAs(linkedSource.Token); loadingComponents ??= new WeakList(); diff --git a/osu.Framework/Utils/SourceGeneratorUtils.cs b/osu.Framework/Utils/SourceGeneratorUtils.cs index 9b857941cf..8e43e4df10 100644 --- a/osu.Framework/Utils/SourceGeneratorUtils.cs +++ b/osu.Framework/Utils/SourceGeneratorUtils.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; -using osu.Framework.Graphics; namespace osu.Framework.Utils { @@ -27,15 +26,8 @@ public static class SourceGeneratorUtils /// If is null. public static void CacheDependency(DependencyContainer container, Type callerType, object? obj, CacheInfo info, Type? asType, string? cachedName, string? propertyName) { - bool allowValueTypes = callerType.Assembly == typeof(Drawable).Assembly; - if (obj == null) - { - if (allowValueTypes) - return; - throw new NullDependencyException($"Attempted to cache a null value: {callerType.ReadableName()}.{propertyName}."); - } CacheInfo cacheInfo = new CacheInfo(info.Name ?? cachedName); @@ -45,7 +37,7 @@ public static void CacheDependency(DependencyContainer container, Type callerTyp cacheInfo = new CacheInfo(cacheInfo.Name ?? propertyName, info.Parent); } - container.CacheAs(asType ?? obj.GetType(), cacheInfo, obj, allowValueTypes); + container.CacheAs(asType ?? obj.GetType(), cacheInfo, obj); } /// @@ -84,7 +76,7 @@ public static T GetDependency(IReadOnlyDependencyContainer container, Type ca { object? val = container.Get(type, new CacheInfo(cachedName, cachedParent)); - if (val == null && !allowNulls) + if (val == null && !type.IsValueType && !allowNulls) throw new DependencyNotRegisteredException(callerType, type); if (rebindBindables && val is IBindable bindableVal) From 5ee40fd424135b046244c3a526939caae8367064 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 29 Jan 2024 18:16:16 +0900 Subject: [PATCH 2/4] Use exception helper Co-authored-by: Berkan Diler --- osu.Framework/Allocation/DependencyContainer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Framework/Allocation/DependencyContainer.cs b/osu.Framework/Allocation/DependencyContainer.cs index b05c62576c..10da5b4495 100644 --- a/osu.Framework/Allocation/DependencyContainer.cs +++ b/osu.Framework/Allocation/DependencyContainer.cs @@ -39,8 +39,7 @@ public void Cache(object instance) /// Extra information to identify in the cache. public void Cache(object instance, CacheInfo info) { - if (instance == null) - throw new ArgumentNullException(nameof(instance)); + ArgumentNullException.ThrowIfNull(instance); CacheAs(instance.GetType(), info, instance); } From 8ff8f8caa494315298e19833127a0260992c220b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 29 Jan 2024 18:19:29 +0900 Subject: [PATCH 3/4] Properly hint for notnull type requirements --- .../Reflection/DependencyContainerTest.cs | 17 +++++++++++---- .../DependencyContainerTest.cs | 17 +++++++++++---- .../Allocation/DependencyContainer.cs | 21 +++++++++++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs b/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs index 4bee79d3ed..924f6532d5 100644 --- a/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs +++ b/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs @@ -301,10 +301,7 @@ public void TestDependenciesOverrideParent(string name, Type parent) dependencies = new DependencyContainer(dependencies); dependencies.CacheAs(2, info); - Assert.Multiple(() => - { - Assert.AreEqual(2, dependencies.Get(info)); - }); + Assert.Multiple(() => { Assert.AreEqual(2, dependencies.Get(info)); }); } [Test] @@ -333,8 +330,19 @@ public void TestResolveNullStruct() Assert.That(new DependencyContainer().Get(), Is.Null); } + [Test] + public void TestModifyBoxedStruct() + { + var dependencies = new DependencyContainer(); + dependencies.CacheAs(new BaseStructObject { TestValue = 1 }); + dependencies.Get().TestValue = 2; + + Assert.That(dependencies.Get().TestValue, Is.EqualTo(2)); + } + private interface IBaseInterface { + int TestValue { get; set; } } private class BaseObject @@ -344,6 +352,7 @@ private class BaseObject private struct BaseStructObject : IBaseInterface { + public int TestValue { get; set; } } private class DerivedObject : BaseObject diff --git a/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs b/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs index 9aaa9355e5..30ed15d9db 100644 --- a/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs +++ b/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs @@ -304,10 +304,7 @@ public void TestDependenciesOverrideParent(string name, Type parent) dependencies = new DependencyContainer(dependencies); dependencies.CacheAs(2, info); - Assert.Multiple(() => - { - Assert.AreEqual(2, dependencies.Get(info)); - }); + Assert.Multiple(() => { Assert.AreEqual(2, dependencies.Get(info)); }); } [Test] @@ -336,8 +333,19 @@ public void TestResolveNullStruct() Assert.That(new DependencyContainer().Get(), Is.Null); } + [Test] + public void TestModifyBoxedStruct() + { + var dependencies = new DependencyContainer(); + dependencies.CacheAs(new BaseStructObject { TestValue = 1 }); + dependencies.Get().TestValue = 2; + + Assert.That(dependencies.Get().TestValue, Is.EqualTo(2)); + } + private interface IBaseInterface { + int TestValue { get; set; } } private class BaseObject @@ -347,6 +355,7 @@ private class BaseObject private struct BaseStructObject : IBaseInterface { + public int TestValue { get; set; } } private class DerivedObject : BaseObject diff --git a/osu.Framework/Allocation/DependencyContainer.cs b/osu.Framework/Allocation/DependencyContainer.cs index 10da5b4495..30a77fb412 100644 --- a/osu.Framework/Allocation/DependencyContainer.cs +++ b/osu.Framework/Allocation/DependencyContainer.cs @@ -44,11 +44,29 @@ public void Cache(object instance, CacheInfo info) CacheAs(instance.GetType(), info, instance); } + /// + /// Caches an instance of a type as its most derived type. This instance will be returned each time you . + /// + /// The instance to cache. + public void Cache(T instance) + where T : notnull + => Cache(instance, default); + + /// + /// Caches an instance of a type as its most derived type. This instance will be returned each time you . + /// + /// The instance to cache. + /// Extra information to identify in the cache. + public void Cache(T instance, CacheInfo info) + where T : notnull + => CacheAs(instance.GetType(), info, instance); + /// /// Caches an instance of a type as a type of . This instance will be returned each time you . /// /// The instance to cache. Must be or derive from . public void CacheAs(T instance) + where T : notnull => CacheAs(instance, default); /// @@ -57,6 +75,7 @@ public void CacheAs(T instance) /// The instance to cache. Must be or derive from . /// Extra information to identify in the cache. public void CacheAs(T instance, CacheInfo info) + where T : notnull => CacheAs(typeof(T), info, instance); /// @@ -65,6 +84,7 @@ public void CacheAs(T instance, CacheInfo info) /// The type to cache as. /// The instance to cache. Must be or derive from . public void CacheAs(Type type, T instance) + where T : notnull => CacheAs(type, instance, default); /// @@ -74,6 +94,7 @@ public void CacheAs(Type type, T instance) /// The instance to cache. Must be or derive from . /// Extra information to identify in the cache. public void CacheAs(Type type, T instance, CacheInfo info) + where T : notnull => CacheAs(type, info, instance); /// From 4082fe3e31e59c4acac2ba094e72a7b87d864c61 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 8 Feb 2024 00:39:30 +0900 Subject: [PATCH 4/4] Update tests around invalid usages --- .../Reflection/CachedAttributeTest.cs | 15 ++++-------- .../Reflection/DependencyContainerTest.cs | 24 ------------------- .../Reflection/ResolvedAttributeTest.cs | 22 ----------------- .../SourceGeneration/CachedAttributeTest.cs | 15 ++++-------- .../DependencyContainerTest.cs | 24 ------------------- .../SourceGeneration/ResolvedAttributeTest.cs | 22 ----------------- 6 files changed, 8 insertions(+), 114 deletions(-) diff --git a/osu.Framework.Tests/Dependencies/Reflection/CachedAttributeTest.cs b/osu.Framework.Tests/Dependencies/Reflection/CachedAttributeTest.cs index 27817b34f9..2969ca90eb 100644 --- a/osu.Framework.Tests/Dependencies/Reflection/CachedAttributeTest.cs +++ b/osu.Framework.Tests/Dependencies/Reflection/CachedAttributeTest.cs @@ -162,20 +162,13 @@ public void TestGetNullInternal() Assert.AreEqual(default(int), new DependencyContainer().Get()); } - /// - /// Test caching a nullable, where the providing type is within the osu.Framework assembly. - /// - [TestCase(null)] - [TestCase(10)] - public void TestCacheNullableInternal(int? testValue) + [Test] + public void TestAttemptCacheNullableInternal() { var provider = new CachedNullableProvider(); + provider.SetValue(null); - provider.SetValue(testValue); - - var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); - - Assert.AreEqual(testValue, dependencies.Get()); + Assert.Throws(() => DependencyActivator.MergeDependencies(provider, new DependencyContainer())); } [Test] diff --git a/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs b/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs index 924f6532d5..3488c78a20 100644 --- a/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs +++ b/osu.Framework.Tests/Dependencies/Reflection/DependencyContainerTest.cs @@ -220,22 +220,6 @@ public void TestReceiveStructInternal() Assert.AreEqual(testObject.CachedObject.Value, receiver.TestObject.Value); } - [TestCase(null)] - [TestCase(10)] - public void TestResolveNullableInternal(int? testValue) - { - var receiver = new Receiver11(); - - var testObject = new CachedNullableProvider(); - testObject.SetValue(testValue); - - var dependencies = DependencyActivator.MergeDependencies(testObject, new DependencyContainer()); - - dependencies.Inject(receiver); - - Assert.AreEqual(testValue, receiver.TestObject); - } - [Test] public void TestAttemptCacheNullInternal() { @@ -435,14 +419,6 @@ private class Receiver10 : IDependencyInjectionCandidate private void load(CachedStructProvider.Struct testObject) => TestObject = testObject; } - private class Receiver11 : IDependencyInjectionCandidate - { - public int? TestObject { get; private set; } - - [BackgroundDependencyLoader] - private void load(int? testObject) => TestObject = testObject; - } - private class Receiver12 : IDependencyInjectionCandidate { public int TestObject { get; private set; } = 1; diff --git a/osu.Framework.Tests/Dependencies/Reflection/ResolvedAttributeTest.cs b/osu.Framework.Tests/Dependencies/Reflection/ResolvedAttributeTest.cs index abc9e33c13..eb643d5ada 100644 --- a/osu.Framework.Tests/Dependencies/Reflection/ResolvedAttributeTest.cs +++ b/osu.Framework.Tests/Dependencies/Reflection/ResolvedAttributeTest.cs @@ -142,22 +142,6 @@ public void TestResolveInternalStruct() Assert.AreEqual(testObject.CachedObject.Value, receiver.Obj.Value); } - [TestCase(null)] - [TestCase(10)] - public void TestResolveNullableInternal(int? testValue) - { - var receiver = new Receiver13(); - - var testObject = new CachedNullableProvider(); - testObject.SetValue(testValue); - - var dependencies = DependencyActivator.MergeDependencies(testObject, new DependencyContainer()); - - dependencies.Inject(receiver); - - Assert.AreEqual(testValue, receiver.Obj); - } - [Test] public void TestResolveStructWithoutNullPermits() { @@ -327,12 +311,6 @@ private class Receiver12 : IDependencyInjectionCandidate public CachedStructProvider.Struct Obj { get; private set; } } - private class Receiver13 : IDependencyInjectionCandidate - { - [Resolved] - public int? Obj { get; private set; } - } - private class Receiver14 : IDependencyInjectionCandidate { [Resolved] diff --git a/osu.Framework.Tests/Dependencies/SourceGeneration/CachedAttributeTest.cs b/osu.Framework.Tests/Dependencies/SourceGeneration/CachedAttributeTest.cs index 29fea93bc4..b5c4b8de62 100644 --- a/osu.Framework.Tests/Dependencies/SourceGeneration/CachedAttributeTest.cs +++ b/osu.Framework.Tests/Dependencies/SourceGeneration/CachedAttributeTest.cs @@ -159,20 +159,13 @@ public void TestGetValueNullInternal() Assert.AreEqual(default(int), new DependencyContainer().Get()); } - /// - /// Test caching a nullable, where the providing type is within the osu.Framework assembly. - /// - [TestCase(null)] - [TestCase(10)] - public void TestCacheNullableInternal(int? testValue) + [Test] + public void TestAttemptCacheNullableInternal() { var provider = new PartialCachedNullableProvider(); + provider.SetValue(null); - provider.SetValue(testValue); - - var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); - - Assert.AreEqual(testValue, dependencies.Get()); + Assert.Throws(() => DependencyActivator.MergeDependencies(provider, new DependencyContainer())); } [Test] diff --git a/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs b/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs index 30ed15d9db..a33526da16 100644 --- a/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs +++ b/osu.Framework.Tests/Dependencies/SourceGeneration/DependencyContainerTest.cs @@ -223,22 +223,6 @@ public void TestReceiveStructInternal() Assert.AreEqual(testObject.CachedObject.Value, receiver.TestObject.Value); } - [TestCase(null)] - [TestCase(10)] - public void TestResolveNullableInternal(int? testValue) - { - var receiver = new Receiver11(); - - var testObject = new PartialCachedNullableProvider(); - testObject.SetValue(testValue); - - var dependencies = DependencyActivator.MergeDependencies(testObject, new DependencyContainer()); - - dependencies.Inject(receiver); - - Assert.AreEqual(testValue, receiver.TestObject); - } - [Test] public void TestAttemptCacheNullInternal() { @@ -439,14 +423,6 @@ private partial class Receiver10 : IDependencyInjectionCandidate private void load(PartialCachedStructProvider.Struct testObject) => TestObject = testObject; } - private partial class Receiver11 : IDependencyInjectionCandidate - { - public int? TestObject { get; private set; } - - [BackgroundDependencyLoader] - private void load(int? testObject) => TestObject = testObject; - } - private partial class Receiver12 : IDependencyInjectionCandidate { public int TestObject { get; private set; } = 1; diff --git a/osu.Framework.Tests/Dependencies/SourceGeneration/ResolvedAttributeTest.cs b/osu.Framework.Tests/Dependencies/SourceGeneration/ResolvedAttributeTest.cs index 8f90fa91e9..145dab7ba0 100644 --- a/osu.Framework.Tests/Dependencies/SourceGeneration/ResolvedAttributeTest.cs +++ b/osu.Framework.Tests/Dependencies/SourceGeneration/ResolvedAttributeTest.cs @@ -139,22 +139,6 @@ public void TestResolveInternalStruct() Assert.AreEqual(testObject.CachedObject.Value, receiver.Obj.Value); } - [TestCase(null)] - [TestCase(10)] - public void TestResolveNullableInternal(int? testValue) - { - var receiver = new Receiver13(); - - var testObject = new PartialCachedNullableProvider(); - testObject.SetValue(testValue); - - var dependencies = DependencyActivator.MergeDependencies(testObject, new DependencyContainer()); - - dependencies.Inject(receiver); - - Assert.AreEqual(testValue, receiver.Obj); - } - [Test] public void TestResolveStructWithoutNullPermits() { @@ -318,12 +302,6 @@ private partial class Receiver12 : IDependencyInjectionCandidate public PartialCachedStructProvider.Struct Obj { get; private set; } } - private partial class Receiver13 : IDependencyInjectionCandidate - { - [Resolved] - public int? Obj { get; private set; } - } - private partial class Receiver14 : IDependencyInjectionCandidate { [Resolved]