Связывание идентификаторов GUID для перечисления


Во время работы с DirectShow, я наткнулся на необходимость легко распознавать различные известные наборами идентификаторов GUID. Е. Г.: Есть разные идентификаторы GUID указать сроки форматах: нет, байта, поле, кадр, MediaTime, образец. Работа с перечислениями вместо идентификаторов GUID будет намного полезнее на мой взгляд.

Я решил написать следующий абстрактный класс-оболочку, которая позволяет связать идентификаторы GUID для перечисления. Вам нужно расширить с ее перечисления вы хотите выставить в качестве параметра типа.

/// <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);
}

Как пример, форматы времени:

/// <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 );
    }
}

Простой и простой, но это самый чистый подход? Любые улучшения или причин, почему это окажется ненужным?

Е. Г.: Обратите внимание, что некоторые кандидаты статические функции не могут быть статическими из-за InitializeTypes которой зависит от метода FillTypeList способ.


Пока я согласен с большинством замечаний, я написал этот код, так как я чувствовал, что это может быть ненадо умничать код'. Одним из преимуществ, в возможный сценарий является, однако, следующее:

// Add the found type to the list.
MediaType type = new MediaType
{
    MajorType = new GuidMediaMajorType(amType.majorType),
    SubType = new GuidMediaSubType(amType.subType)
};
break;

Эти типы "завернутый" в GUID тип, который пытается связать их в правильное перечисление, когда нашли. Альтернативой может быть карту неизвестных типов в "неизвестное" значение перечисления, но тогда информация GUID теряется. Это, кажется, единственное преимущество этой оболочки более простой вспомогательный класс с функциями преобразования. Написав этот код, я на самом деле удалось найти скопировать и вставить ошибка в DirectShowLib, как повторяющиеся идентификаторы GUID были объявлены. Конечно, я бы тоже нашел это, используя простой подход словарь.



7554
9
c#
задан 21 марта 2011 в 07:03 Источник Поделиться
Комментарии
2 ответа

Я бы избежать литья то же самое более одного раза. Вы можете просто использовать в качестве, он будет возвращать null, если объект не того типа.

public override bool Equals( object obj )
{
AbstractGuidEnum<T> guidObj = obj as AbstractGuidEnum<T>;
if ( guidObj == null )
{
return false;
}

return Guid.Equals( guidObj.Guid );
}

Если вы собираетесь иметь метод Equals, было бы неплохо реализовать интерфейс IEquatable и интерфейс IEquatable.

Я не вижу нигде, что AbstractGuidEnum.Типа, GUID и IsKnowType установлены, то конструктор. Если это так, то я бы сделал их в свойства, с поддержкой полей и сделать поддержку полей только для чтения.

6
ответ дан 21 марта 2011 в 11:03 Источник Поделиться

Я думаю, что ваше решение нарушает Kiss_principle. Что Бенифит делает ваше решение более простой, но сухой нарушении

public TimeFormat GetType( Guid type )
{
switch (type) {
case DirectShowLib.TimeFormat.None : return TimeFormat.None;
...

public Guid GetType( TimeFormat type )
{
switch (type) {
case TimeFormat.None : return DirectShowLib.TimeFormat.None;
...

См. также
Семь смертельных грехов программирования - грех #6 - ненадо умничать код

3
ответ дан 22 марта 2011 в 08:03 Источник Поделиться