Skip to content

Instantly share code, notes, and snippets.

@martijnburgers
Last active August 29, 2015 14:07
Show Gist options
  • Save martijnburgers/0885c8ae3fd364c885f0 to your computer and use it in GitHub Desktop.
Save martijnburgers/0885c8ae3fd364c885f0 to your computer and use it in GitHub Desktop.
Descriptor implementation
@martijnburgers
Copy link
Author

/// <summary>
/// The base descriptor class.
/// </summary>
/// <typeparam name="TDescriptor">The type of the descriptor</typeparam>
public abstract class BaseDescriptor<TDescriptor> : IDescriptor where TDescriptor : BaseDescriptor<TDescriptor>
{
    static class TypeLock<T>
    {
        public static readonly object SyncLock = new object();            
    }

    static BaseDescriptor()
    {                              
        //force type init for the descriptor
        RuntimeHelpers.RunClassConstructor(typeof(TDescriptor).TypeHandle);            
    }

    private static Dictionary<string, TDescriptor> _dict = null;

    //instance properties
    public string NameOfDescriptor { get; private set; }
    public int? DescriptorGroup { get; private set; }

    /// <summary>
    /// Ctor
    /// </summary>
    /// <param name="nameOfDescriptor"></param>
    /// <param name="descriptorGroup">optional, the descriptor group identifier.</param>
    /// <param name="comparer">The key comparer.</param>
    protected BaseDescriptor(string nameOfDescriptor, int? descriptorGroup = null, IEqualityComparer<string> comparer = null)
    {

        if (string.IsNullOrEmpty(nameOfDescriptor)) throw new ArgumentNullException("nameOfDescriptor");

        InitDictionary(comparer);

        if (comparer == null) comparer = EqualityComparer<string>.Default;

        if (_dict.Comparer.GetType() != comparer.GetType())
            throw new InvalidOperationException("The comparer of the dictionary is different then the given comparer.");

        NameOfDescriptor = nameOfDescriptor;
        DescriptorGroup = descriptorGroup;

        //add to private static dict
        _dict.Add(nameOfDescriptor, (TDescriptor)this);
    }

    /// <summary>
    /// Ctor
    /// </summary>
    /// <param name="nameOfDescriptor"></param>
    /// <param name="descriptorGroup">optional, the descriptor group identifier.</param>
    /// <param name="comparer">The key comparer.</param>
    protected BaseDescriptor(string nameOfDescriptor, Enum descriptorGroup = null, IEqualityComparer<string> comparer = null)
        : this(nameOfDescriptor, descriptorGroup == null ? (int?)null : (int)(object)descriptorGroup, comparer)
    {
    }

    private void InitDictionary(IEqualityComparer<string> comparer = null)
    {
        if (_dict == null)
        {
            lock (TypeLock<TDescriptor>.SyncLock)
            {
                if (_dict == null)
                {
                    _dict = new Dictionary<string, TDescriptor>(comparer);
                }
            }
        }
    }

    /// <summary>
    /// Get's the string representation of the descriptor.
    /// </summary>
    /// <returns>A string containing the name of the descriptor.</returns>
    public override string ToString()
    {
        return NameOfDescriptor;
    }

    /// <summary>
    /// Parses a string value to a typed descriptor. 
    /// </summary>
    /// <param name="name">The string identifier of the descriptor</param>
    /// <returns>If <param name="name"></param> does not exists null otherwise the typed contract type.</returns>        
    public static TDescriptor Parse(string name)
    {
        if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");

        return _dict.Keys.Contains(name) ? _dict[name] : null;
    }

    /// <summary>
    /// Tries to parse a string value to a typed descriptor.
    /// </summary>
    /// <param name="name">The string identifier of a the descriptor</param>
    /// <param name="descriptor">Output parameter which is the resolved descriptor. Null if it could not be parsed.</param>
    /// <returns>true if it could be parsed, false if it could not be parsed.</returns>
    public static bool TryParse(string name, out TDescriptor descriptor)
    {
        return _dict.TryGetValue(name, out descriptor);
    }

    /// <summary>
    /// Get all available descriptors.
    /// </summary>
    /// <returns>A sequence of descriptors.</returns>
    public static IEnumerable<TDescriptor> GetMembers()
    {
        return _dict.Values;
    }

    IEnumerable<IDescriptor> IDescriptor.GetMembers()
    {
        return GetMembers();
    }

    /// <summary>
    /// Get all available descriptors for the given group.
    /// </summary>
    /// <returns>A sequence of descriptors.</returns>
    public static IEnumerable<TDescriptor> GetMembersForGroup(int? descriptorGroup)
    {
        return _dict.Values.Where(a => a.DescriptorGroup == descriptorGroup);
    }

    /// <summary>
    /// Get all available descriptors for the given group.
    /// </summary>
    /// <returns>A sequence of descriptors.</returns>
    public static IEnumerable<TDescriptor> GetMembers(Predicate<TDescriptor> predicate)
    {
        return _dict.Values.Where(a => predicate(a));
    }
}

/// <summary>
/// Interface for defining descriptors
/// </summary>
public interface IDescriptor
{
    /// <summary>
    /// Only filled when a descriptor is annotated with <see cref="DescriptorGroup"/>
    /// </summary>
    int? DescriptorGroup { get; }

    IEnumerable<IDescriptor> GetMembers();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment