Translating template interface from C++ to C#

Started by
1 comment, last by lack o comments 16 years, 11 months ago
I was translating a bit of code from C++ to C# when I came across a minor difference between generics and templates that I wouldn't mind settling. The overall structure is that of an generic asset manager coupled with a generic abstract asset class. A simplified example is this:

class CAssetManager<BASETYPE>
	{
	}
	
abstract class IAsset<TYPE>
	{
	abstract bool Load();
	abstract bool Release();
	IAsset(string FileName,ref CAssetManager<TYPE> Manager)	{}
	}
At this point I can derive a class from IAsset for whatever asset type I want, say, a bitmap (using a made up datatype BITMAPDATA)

class BitmapAsset : IAsset<BITMAPDATA>
	{
	//overriden methods here
	}
Now, I can simply create an instance of a bitmap manager and bitmap assets like so.

CAssetManager<BITMAPDATA> BitmapManager = new CAssetManager<BITMAPDATA>();
BitmapAsset Bitmap = new BitmapAsset("C:/image.txt",ref BitmapManager);
The trouble is, I must declare BITMAPDATA data in the creation of CAssetManager when it would make more sense to declare the type "BitmapAsset" like so.

CAssetManager<BitmapAsset> ...
From here I do not understand how I can, within the manager class, access to type that was passed to the base asset class when creating the derived one. In C++ all I need to do is typdefine it in the asset class and then use the :: operator to access that variable from the type passed to the manager class. Of course, C# doesn't have typedefs but I imagine with all this neat stuff, like generics being 1st class objects and RTTI, I can do something similar. I realize this isn't a big issue but since I'm just starting to learn C# I'd like to know as much as possible about this sort of thing.
Advertisement
Be warned: Generics are not templates nor are they anything like templates. Generics are a runtime construct with a bit of compiletime sanity checking. Templates are an entirely compiletime construct with no runtime anything.

As for your problem, just have your abstract IAsset class have a method that returns a Type obtained through the typeof instruction on the generic-parameter.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Hmmmmm, ok. Now you've lost me. I understand the method returning the type but I can't seem to wrap my head around the syntax needed within the manager class.
Maybe you can point it out for me. I've tried using the "where" contraint for BASETPYE to be derived from "IAsset<TYPE>", "TYPE" must be defined alongside BASETYPE which defeats the purpose of passing a derived class instead of the basetype to begin with.

I've read that generics are based on runtime information many times but I think all those years of C++'s half-assed-everything rotted my brains out. I can't seem to quite comprehend how it works. I can only guess that when a generic type is created it's type is created along with it and treated as an object in its own right.

IAsset.cs
using System;using System.Collections.Generic;using System.Text;[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("CAssetManager")]namespace AssetControl	{	enum RESELEMENTSTATE		{		RES_EMPTY = 0,		RES_RESERVED,		RES_LOADED,		};	enum RESLOCKSTATE		{		RESLSTATE_UNLOCKED,		RESLSTATE_LOCKED,		};			abstract class IAsset<TYPE>		{				private RESELEMENTSTATE		m_State = RESELEMENTSTATE.RES_EMPTY;		private RESLOCKSTATE		m_LockState = RESLOCKSTATE.RESLSTATE_UNLOCKED;		private string				m_FileName;		private TYPE				m_Data;		private CAssetManager<TYPE>	m_Manager;				static public Type MyType()			{			return(typeof(TYPE));			}		protected internal abstract bool	Load(string FileName,out TYPE Data);		protected internal abstract bool	Release();						public string FileName			{			get	{				return(m_FileName);				}			}		public int Lock(out TYPE Data)			{			m_LockState	= RESLOCKSTATE.RESLSTATE_LOCKED;			Data			= m_Data;			return (0);			}		public int Unlock()			{			m_LockState	= RESLOCKSTATE.RESLSTATE_UNLOCKED;			return (0);			}		public IAsset(string FileName,ref CAssetManager<TYPE> Manager)			{			//assert Manager reference			if(Manager == null)				{				throw new Exception(AssetFailCode.ManagerRefNotValid);								}						//set these states first, that way they may			//be accessed within the derived class's "Load()" method			m_State		= RESELEMENTSTATE.RES_LOADED;			m_FileName	= FileName;						//register and assign asset manager classes, making sure			//not to create an asset that already exists			m_Manager		= Manager;			try  {				//this will internally call the "Load()" method,				//and throw an exception if it fails				m_Manager.Register(this,out m_Data);				}			catch(Exception e)				{				//load failed, reset to default states and throw exception				m_State = RESELEMENTSTATE.RES_EMPTY;				m_FileName = null;				throw e;				}								}		~IAsset()			{				//assert that the Manager reference was valid when created			if(m_Manager == null)				{								if(!(Release()))					{										throw new Exception(AssetFailCode.AssetLoadFail);					}				return;				}									try	{				//calls "Release()" internally				m_Manager.Unregister(this);				}			catch(Exception e)				{				throw e;				}						}		}	}


CAssetManager.cs
using System;using System.Collections.Generic;using System.Text;[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("IAsset")]namespace AssetControl	{	class AssetFailCode		{		public const string AssetLoadFail		= "Asset Failed To Load Properly";		public const string AssetReleaseFail	= "Asset Failed To Release Properly";		public const string ManagerRefNotValid	= "Manager Reference Passed As NULL";		}			public class DataRefCountPair<BASETYPE>		{		public BASETYPE	Data;		public int		RefCount;				public DataRefCountPair()				{			}		public DataRefCountPair(BASETYPE data,int refcount)			{			Data		= data;			RefCount	= refcount;			}		}			class CAssetManager<BASETYPE>		{		//maps asset filenames to a reference		//of the asset's data and a refcount		private SortedDictionary<string,DataRefCountPair<BASETYPE>>	m_Database = new SortedDictionary<string,DataRefCountPair<BASETYPE>>();		protected internal void Register(IAsset<BASETYPE> Asset, out BASETYPE Data)			{			DataRefCountPair<BASETYPE>	Entry;									//find out if file was previously loaded by checking database.			//If it was, asign the asset-class's data to that entry			if(!(m_Database.TryGetValue(Asset.FileName,out Entry)))				{				//if the file was not already loaded, load it now				if(Asset.Load(Asset.FileName,out Data))					{										//now assign the loaded asset-class's					//data to the manager's database						Entry = new DataRefCountPair<BASETYPE>(Data,1);							m_Database.Add(Asset.FileName, Entry);					}				else	{					throw new Exception(AssetFailCode.AssetLoadFail);										}				}			else {				//assign asset-class's Data to				//the pre-existing element and				//increment the refcount				Data = m_Database[Asset.FileName].Data;											m_Database[Asset.FileName].RefCount++;				}			}		protected internal void Unregister(IAsset<BASETYPE> Asset)			{			//reduce refcount by one, if refcount reaches zero			//set that element to NULL and remove it from the database			DataRefCountPair<BASETYPE> Entry;			m_Database.TryGetValue(Asset.FileName,out Entry);						if(Entry.RefCount == 1)				{				m_Database.Remove(Asset.FileName);								}			else {								m_Database[Asset.FileName].RefCount--;				}										if(!(Asset.Release()))				{				throw new Exception(AssetFailCode.AssetReleaseFail);				}			}			}		}


BitmapAsset.cs (in this case I was testing by readin in a byte froma file and storing it in an int)
using System;using System.Collections.Generic;using System.Text;using AssetControl;using System.IO;namespace AssetFactory	{	class BitmapAsset : IAsset<int>		{		protected internal override bool Load(string FileName,out int Data)			{			//FileStream f = new FileStream(FileName,FileMode.Create,FileAccess.Write);			FileStream f = new FileStream(FileName,FileMode.Open,FileAccess.Read);			byte[] data = new byte[1];			data[0] = 99;			//f.Write(data,0,1);			f.Read(data,0,1);						Data = data[0];						Console.WriteLine("-File Loaded\nFile Name: {0}\nValue Read: {1}\n",FileName,data[0]);						return true;			}		protected internal override bool Release()			{			Console.WriteLine("-Bitmap Released Successfully");			return true;			}		public BitmapAsset(string FileName,ref CAssetManager<int> Manager) : base(FileName,ref Manager)			{					}		}	}

This topic is closed to new replies.

Advertisement