This blog is no longer updated. Feel free to copy any useful information to other blogs or wikis as this site may not exist for much longer. Thanks.


Naming your content

February 27th, 2010 | 1 comment

A number of the built in types you load through ContentManager in XNA have a Name property. Most (all?) don’t actually set this at any point; it’s just there for you to use. I like to use these names for various things and I’m sick of setting them all manually. So I started out to make my own ContentManager that does this for me.

It started off with something like this:

public class MyContentManager : ContentManager
{
	public MyContentManager(IServiceProvider services)
		: base(services, "Content")
	{
	}

	public override T Load<T>(string assetName)
	{
		T content = base.Load<T>(assetName);

		if (content is GraphicsResource)
		{
			(content as GraphicsResource).Name = assetName;
		}
		else if (content is SoundEffect)
		{
			(content as SoundEffect).Name = assetName;
		}

		return content;
	}
}

And that works. But what if I miss some content? What if I add my own custom type with a Name property? On my second go I decided to use a reflection based approach by just seeing if the object has a settable Name property:

public class MyContentManager : ContentManager
{
	public MyContentManager(IServiceProvider services)
		: base(services, "Content")
	{
	}

	public override T Load<T>(string assetName)
	{
		T content = base.Load<T>(assetName);

		PropertyInfo nameProperty = typeof(T).GetProperty("Name", typeof(string));
		if (nameProperty != null && nameProperty.CanWrite)
		{
			nameProperty.SetValue(content, assetName, null);
		}

		return content;
	}
}

And now any content that I load that has a Name property with a setter will get its name set. But I’m not quite done yet. Reflection isn’t the quickest thing in the world and I do like to use the ContentManager as a cache. This implementation will set the Name property each time the content is pulled from the ContentManager. While this is a good thing if you plan to change the name other places, it can be a performance hit. So I decided to add a dictionary to track what content has already been named and avoid naming things twice:

public class MyContentManager : ContentManager
{
	private readonly Dictionary<string, bool> namedContent = new Dictionary<string, bool>();

	public MyContentManager(IServiceProvider services)
		: base(services, "Content")
	{
	}

	public override T Load<T>(string assetName)
	{
		T content = base.Load<T>(assetName);

		bool named = false;
		if (!namedContent.TryGetValue(assetName, out named) || !named)
		{
			PropertyInfo nameProperty = typeof(T).GetProperty("Name", typeof(string));
			if (nameProperty != null && nameProperty.CanWrite)
			{
				nameProperty.SetValue(content, assetName, null);
			}
			namedContent.Add(assetName, true);
		}

		return content;
	}
}

And that’s where I’m at now. I fully realize that reflection is not going to be as fast as doing those casts, but since I’m also doing this caching of what items have been named, I’m only going to see that performance hit the first time I load an object and generally loading the object takes enough time that the extra cost of the reflection isn’t likely to be noticeable or an issue.

I’d be interested in hearing what others are doing to extend this class. Anyone else using a custom ContentManager for anything?


Possibly Related Posts

(Automatically Generated)
Exception handling and GamerServicesComponent
Generic UI for parameter editing
My Singleton Base Class
XmlSerializer Misconceptions Part 1
Creating Your Own XNB Files

  1. February 27th, 2010 at 10:40
    Quote | #1

    I have a custom class ContentLoader which does not special. It implements IContentLoader though so when a class needs to load content, I can mock the content loading process in unit tests.

Comments are closed.