-
Notifications
You must be signed in to change notification settings - Fork 422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow value types to be cached and injected as dependencies #6159
base: master
Are you sure you want to change the base?
Conversation
Co-authored-by: Berkan Diler <[email protected]>
d7899ee
to
8ff8f8c
Compare
Why are tests failing here? I'd expect failures in the sourcegen path until a package bump or something, but the reflection path is failing too? |
Sourcegen won't need updates; I just forgot to update a test. Will get to it. |
Reading the OP: Cache<int?>(0); // Warning in NRT contexts Why would this warn? Looks like perfectly legal code to me... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rather difficult diff to get through
|
||
/// <summary> | ||
/// Caches an instance of a type as a type of <paramref name="type"/>. This instance will be returned each time you <see cref="Get(Type)"/>. | ||
/// Caches an instance of a type as its most derived type. This instance will be returned each time you <see cref="Get{T}()"/>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're editing these, I'd probably clarify further to say
/// Caches an instance of a type as its most derived type. This instance will be returned each time you <see cref="Get{T}()"/>. | |
/// Caches an instance of a type as its most derived type as returned by <see cref="object.GetType"/>. This instance will be returned each time you <see cref="Get{T}()"/>. |
(goes for all other overloads of this)
object? Get(Type type); | ||
|
||
object? Get(Type type, CacheInfo info); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are these for? It's not super clear from reading diff but seeing object?
return type on public API doesn't feel great...
|
||
public T Get<T>(CacheInfo info) | ||
{ | ||
TryGet(out T value, info); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A TryGet()
call with no return value check? What's going on here? Isn't this gonna end up being wrong for reference types?
@@ -149,29 +153,22 @@ public void TestCacheStructInternal() | |||
|
|||
var dependencies = DependencyActivator.MergeDependencies(provider, new DependencyContainer()); | |||
|
|||
Assert.AreEqual(provider.CachedObject.Value, dependencies.GetValue<CachedStructProvider.Struct>().Value); | |||
Assert.AreEqual(provider.CachedObject.Value, dependencies.Get<CachedStructProvider.Struct>().Value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is pretty much superseded by TestCacheStruct
it looks like. Probably can be removed.
{ | ||
Assert.AreEqual(default(int), new DependencyContainer().GetValue<int>()); | ||
Assert.AreEqual(default(int), new DependencyContainer().Get<int>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This creates a duality in semantics in that a "required value-type dependency" does not practically exist. If you forget to cache one, anyone that tries to resolve it will just get default
back.
If the argument here is that that is fine and matches the general type semantics of C# in general, that's fine, but should probably be documented better.
var receiver = new Receiver14(); | ||
|
||
Assert.DoesNotThrow(() => new DependencyContainer().Inject(receiver)); | ||
Assert.AreEqual(0, receiver.Obj); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, this test is basically TestResolveDefaultStruct
isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are there changes in this test but not the reflection variant of it?
Almost thinking all of these tests need to be rewritten... |
Resolves #6157
Consider this an RFC.
The way structs are handled right now is a mess. If I (as the person who wrote all of this) can't understand how they're handled, then I don't think anyone can. And my confusion is the result of two conflicting thought processes:
CancellationToken
is somehow a special type. How is it so?Every value type is a special type that can only be cached by osu!framework.
CancellationToken
is doubly special because it sometimes arrives inload()
, and sometimes doesn't, depending on whether you're in an async-load context. So the only proper way to use BDL withCancellationToken
is to make it nullable.In my opinion, if you want to cache and resolve a value-type dependency and accept whatever footguns that comes with (by-val copies, you can't cache multiple
int
s, etc), then you should be able to do so. But the framework should retain predictable semantics, which for me means matching the C# spec -ValueType = default!
is a zero-initialised struct. This is a simplification of semantics to me.New semantics:
Is this something I foresee being used? No. It's just for the sake of clarifying semantics.