When any resource (for example a Texture2D or Effect) is loaded through a ContentManager instance, is then Dispose()d explicitly at a later time, there is no way to load this resource through the same ContentManager instance again.
Example code:
var test = Content.Load<Texture2D>("test");
....
test.Dispose();
....
test = Content.Load<Texture2D>("test");
The texture resource _"test"_ in this example can never be loaded through the same ContentManager again because it has been disposed and the ContentManager keeps a record of all resources loaded which makes it return the same disposed resource instance.
I have tested this, and it works the same way with XNA, but I am wondering if this is intended behavior with disposed resources, because besides creating a new ContentManager instance, I don't see a way of loading a disposed resource at a later time.
And to do this, I need to keep a record of disposed resources per ContentManager myself to predict when I need a new ContentManager just to load a disposed resource.
My opinion on this is that when I call Load<T>, I do NOT expect it to return an already disposed resource, even if I disposed this resource earlier, as there is no way to remove this disposed resource instance from the internal ContentManager record.
I have tested this, and it works the same way with XNA, but I am wondering if this is intended behavior
It is intended behavior.
Content loaded via the ContentManager is disposed when ContentManager.Dispose() or ContentManager.Unload().
We really cannot keep people from messing up content loaded via the content manager. Like your example... disposing the content... or you could call SetData on the texture and modify it affecting everyone else using that content.
I wish there was a way to make content returned from the content manager "const" so it cannot be modified... but no such mechanism exists in C#.
My opinion on this is that when I call
Load<T>, I do NOT expect it to return an already disposed resource,
I agree... but you disposed it... I suggest not doing that.
If we wanted to break with XNA compatibility and check if a resource is disposed and if so load a new instance of the content. Unfortunately IDisposable does not expose any method to check if something is disposed. The best we can do is GraphicsResource.IsDisposed.
Still IMO... don't dispose content manager resources.
@KonajuGames @dellis1972
I realized that XNA was conceptualized to load all resources at the beginning of the execution of the program (LoadContent method in the Game class), but this is far from realistic.
When I have a part in my game that requires resources for a short time multiple times during the game's runtime, I don't want to keep those resources around when I don't need them.
Would the best practice be to create a ContentManager for this part of the game?
Would the best practice be to create a
ContentManagerfor this part of the game?
Exactly... XNA was designed to allow you to create multiple ContentManagers. Generally games use Game.Content as global content that is never unloaded. They then keep an additional one or more content managers for different parts of the game when they need to unload content.
Alright, thanks a lot.
Would it maybe make sense to add some sort of documentation to GraphicsResource.Dispose (or any resource loaded through the ContentManager) so people know not to dispose it and instead call ContentManager.Dispose?
@nilllzz Just customize ContentManager and implement your own reference tracking (or not).
https://blogs.msdn.microsoft.com/shawnhar/2007/03/09/contentmanager-readasset/
@nkast I snug my way around the resource disposing by using multiple content managers.
What I was talking about is that other people might also fall for this "trap", and that it wasn't easy to debug because it doesn't seem like an expected behavior.
My code crashed on a SpriteBatch.End() for example, with just a NullReferenceException.
Figuring out that this came from GraphicsDevice being null in the Effect instance I used in the SpriteBatch that I disposed minutes earlier in the game's runtime took a little doing.
My question remains if it would be preferable to add a <summary> tag to all standard MonoGame classes loaded through the ContentManager with a Dispose method detailing the situation.
XNA had more checks for disposed objects that threw
ObjectDisposedException. You don't want too many of these checks though
because each one adds a little bit of time to the execution time of the
method. Have several thousand of these every frame and it could add up.
Maybe do checks like this in Debug mode only? If this confuses users a more descriptive exception can help out.
It would make sense for when disposable resources are used inside MonoGame's classes, for example the SpriteBatcher.
Checking for Effect.IsDisposed only happens in DrawBatch, so once every time a SpriteBatch renders to the GPU. That shouldn't have a huge effect on performance.
Of course, this example is the one I encountered the problem with, one would have to search for more of these.
The summary is that we should add more checks, but be mindful of where we
add them.
The summary is that we should add more checks, but be mindful of where we
add them.
Instead of checking every time, we can use a try/catch block to to catch exceptions and if it's due to disposed items then throw an ObjectDisposedException, otherwise rethrow the original exception.
ex:
{
//...
}
catch(Exception ex)
{
if (resource.IsDisposed)
throw new ObjectDisposedException("resource")
else
throw;
}