While working with DirectShow, I came across the need to easily recognize different known 'sets' of GUIDs. E.g.: There are different GUIDs to indicate the possible time formats: None, Byte, Field, Frame, MediaTime, Sample
. Working with enums instead of GUIDs would be a lot more useful in my opinion.
I decided to write the following abstract wrapper class which allows linking GUIDs to enums. You need to extend from it with the enum you want to expose as a type parameter.
/// <summary>
/// An abstract class to represent a set of GUIDs as an enum.
/// </summary>
/// <typeparam name="T">An enum to use for the type to expose.</typeparam>
abstract class AbstractGuidEnum<T>
{
/// <summary>
/// The internal GUID.
/// </summary>
public Guid Guid
{
get; private set;
}
/// <summary>
/// The enum type for this GUID.
/// </summary>
public T Type
{
get;
private set;
}
/// <summary>
/// Specifies whether the GUID is known as a specific type or not.
/// </summary>
public bool IsKnownType
{
get; private set;
}
/// <summary>
/// List matching GUIDs with the enum types.
/// </summary>
private static Dictionary<Guid, T> m_typeList;
/// <summary>
/// Create a new GUID enum wrapping the given GUID.
/// </summary>
/// <param name="guid"></param>
protected AbstractGuidEnum( Guid guid )
{
Guid = guid;
if ( IsGuidKnownType( guid ) )
{
IsKnownType = true;
Type = GetType( guid );
}
else
{
IsKnownType = false;
}
}
/// <summary>
/// Create a new GUID enum for the specified type.
/// </summary>
/// <param name="type"></param>
protected AbstractGuidEnum( T type )
{
Type = type;
Guid = GetGuid( type );
}
/// <summary>
/// Call to make sure the list of types is initialized.
/// </summary>
private void InitializeTypes()
{
if ( m_typeList == null )
{
m_typeList = new Dictionary<Guid, T>();
FillTypeList( m_typeList );
}
}
public bool IsGuidKnownType( Guid guid )
{
InitializeTypes();
return m_typeList.ContainsKey( guid );
}
/// <summary>
/// Returns the type for a given GUID.
/// </summary>
/// <param name="type">The GUID to get the type for.</param>
/// <returns>The type for the given GUID.</returns>
public T GetType( Guid type )
{
InitializeTypes();
if ( !m_typeList.ContainsKey( type ) )
{
throw new ArgumentException("No type is defined for the given GUID.", "type");
}
return m_typeList[ type ];
}
/// <summary>
/// Return the guid for a given type.
/// </summary>
/// <param name="type">The Type to get the Guid for.</param>
/// <returns>The Guid for the given type.</returns>
/// <exception cref="InvalidCastException">
/// Thrown when no Guid exists for the given type.
/// </exception>
private Guid GetGuid( T type )
{
InitializeTypes();
try
{
return m_typeList.Keys.First( guid => m_typeList[ guid ].Equals( type ) );
}
catch ( InvalidOperationException )
{
throw new InvalidCastException( "No Guid exists for the given type." );
}
}
public override bool Equals( object obj )
{
if ( !(obj is AbstractGuidEnum<T>) )
{
return false;
}
AbstractGuidEnum<T> guidObj = obj as AbstractGuidEnum<T>;
return Guid.Equals( guidObj.Guid );
}
public override int GetHashCode()
{
unchecked
{
return Guid.GetHashCode();
}
}
/// <summary>
/// Fill up the list which matches GUIDs to the desired enum types.
/// </summary>
/// <param name="typeList"></param>
protected abstract void FillTypeList(Dictionary<Guid, T> typeList);
}
As an example, the time formats:
/// <summary>
/// The possible time formats for seeking operations.
/// </summary>
public enum TimeFormat
{
None,
Byte,
Field,
Frame,
MediaTime,
Sample
}
/// <summary>
/// A wrapper class which represents the possible media types for DirectShow.
/// Since a MediaType is a Guid, a simple enum couldn't be used.
/// </summary>
class GuidTimeFormat : AbstractGuidEnum<TimeFormat>
{
/// <summary>
/// Create a new GuidTimeFormat wrapping the given GUID.
/// </summary>
/// <param name="guid">The GUID to wrap.</param>
public GuidTimeFormat( Guid guid ) : base( guid )
{
}
public GuidTimeFormat( TimeFormat timeFormat ) : base( timeFormat )
{
}
protected override void FillTypeList( Dictionary<Guid, TimeFormat> typeList )
{
typeList.Add( DirectShowLib.TimeFormat.None, TimeFormat.None );
typeList.Add( DirectShowLib.TimeFormat.Byte, TimeFormat.Byte );
typeList.Add( DirectShowLib.TimeFormat.Field, TimeFormat.Field );
typeList.Add( DirectShowLib.TimeFormat.Frame, TimeFormat.Frame );
typeList.Add( DirectShowLib.TimeFormat.MediaTime, TimeFormat.MediaTime );
typeList.Add( DirectShowLib.TimeFormat.Sample, TimeFormat.Sample );
}
}
Plain and simple, but is this the cleanest approach? Any improvements, or reasons why this would be unnecessary?
E.g.: Notice that some candidate static functions can't be made static because of InitializeTypes
which relies on the overridable FillTypeList
method.
So far I agree with most of the comments made, I posted this code since I felt it might be 'inappropriately clever code'. One of the benefits, in a possible scenario is the following however:
// Add the found type to the list.
MediaType type = new MediaType
{
MajorType = new GuidMediaMajorType(amType.majorType),
SubType = new GuidMediaSubType(amType.subType)
};
break;
The types are 'wrapped' in the guid type, which tries to link them up to the correct enum, when found. The alternative would be to map unknown types to an 'Unknown' enum value, but then the guid information is lost. This does seem to be the only benefit of this wrapper over a simple helper class with conversion functions. By writing this code I actually managed to find a copy/paste bug in DirectShowLib, as duplicate GUIDs were declared. Ofcourse, I would have also found this using a simple dictionary approach.
Guid
? I can imagine easier ways to mapenum
intoGuid
if you do not it to be truly random. \$\endgroup\$TimeFormat
enum primarily in your code andGuidTimeFormat
will be used only where you need to map enum into DirectShow guid? I have a strong feeling that this is made to complicated but until I get how are going to use it I can't fully express that feeling \$\endgroup\$