I am currently working on a project that requires a common interface for defining, sharing, and lookup of a hierarchy of objects. Essentially, there would be a shared project that defines a common structure for all objects, and consuming projects can use it to define objects and share them among other projects. I am wondering if there is an existing design pattern that fits this and implements this well.
I have already attempted this, but I'm not exactly satisfied with the results. My current approach defines a Tag
class, which uusesa unique string
as the name, and a Guid
as a runtime-generated Id for faster lookup (hashing and equating Guid
values is much faster than doing so with strings). Then, objects that I want to be shared among projects globally define that Tag
as a member.
In this current design, Tag
objects serve solely as an identifier, and contain no data. I would like to re-design this so that tags DO contain data and serve as a base class. However, this is becoming increasingly complex, and so I am hoping to find established design patterns or existing projects that implement a similar system to aid in development.
[DebuggerDisplay( "Name = {Name}, Children = {ChildCount}" )]
public class Tag : IDisposable, IEquatable<Tag>
{
#region Data Members
public readonly Guid Id;
public readonly string Name;
public readonly string FullName;
public readonly Tag Parent;
protected readonly HashSet<Tag> children_;
protected readonly int hash_;
protected bool isDisposed_;
#endregion
#region Properties
public IReadOnlyCollection<Tag> Children
{
[MethodImpl( MethodImplOptions.AggressiveInlining )]
get => children_;
}
public int ChildCount
{
[MethodImpl( MethodImplOptions.AggressiveInlining )]
get => children_.Count;
}
public bool HasChildren
{
[MethodImpl( MethodImplOptions.AggressiveInlining )]
get => children_.Count > 0;
}
public bool HasParent
{
[MethodImpl( MethodImplOptions.AggressiveInlining )]
get => Parent != null;
}
#endregion
#region Constructor
public Tag( string name )
: this( Guid.NewGuid(), name, null )
{
}
public Tag( string name, Tag parent )
: this( Guid.NewGuid(), name, parent )
{
}
public Tag( Guid id, string name )
: this( id, name, null )
{
}
public Tag( Guid id, string name, Tag parent )
{
Id = id;
FullName = Name = name;
children_ = new HashSet<Tag>();
hash_ = Name.GetHashCode( StringComparison.InvariantCultureIgnoreCase );
if( parent != null )
{
Parent = parent;
FullName = $"{parent.FullName}.{Name}";
Parent.AddChild( this );
}
}
~Tag()
{
Dispose( false );
}
#endregion
#region Public Methods
[MethodImpl( MethodImplOptions.AggressiveInlining )]
internal void AddChild( Tag child )
{
if( !children_.Add( child ) )
{
children_.TryGetValue( child, out var existingChild );
throw new DuplicateTagChildException( this, existingChild, child );
}
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
internal void RemoveChild( Tag child )
{
children_.Remove( child );
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public bool HasChild( Tag child )
{
return children_.Contains( child );
}
#endregion
#region Overrides
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public bool Equals( Tag other )
{
return Name.Equals( other.Name, StringComparison.InvariantCultureIgnoreCase );
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public override bool Equals( object obj )
{
return obj is Tag other
&& Name.Equals( other.Name, StringComparison.InvariantCultureIgnoreCase );
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public override int GetHashCode()
{
return hash_;
}
#endregion
#region IDisposable Methods
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
public virtual void Dispose( bool disposing )
{
if( isDisposed_ )
return;
if( disposing )
{
}
Parent?.RemoveChild( this );
TagCache.TryRemove( this );
isDisposed_ = true;
}
#endregion
}
Aucun commentaire:
Enregistrer un commentaire