.NET Fiddle

CSharp · Console

/Program.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class Program
{
	public static void Main()
	{
		var tree = new NamespaceDirectoryTree
		{
			"Root.Sub11.Sub11.Sub3",
			"Root.Sub11.Sub11",
			"Root.Sub11.Sub21",
			"Root.Sub11.Sub21.Sub12",
			"Root.Sub11.Sub21.Sub13.Sub14",
			"Root.Sub11.Sub21.Sub13.Sub14",
			"Root.Sub11.Sub21.Sub13.Sub14",
			"Root.Sub11.Sub21.Sub13.Sub14.Sub47"
		};

		foreach(var directory in tree)
		{
			System.Console.WriteLine(directory + ", ");
		}
	}
	
	
    public class NamespaceDirectoryTree : ICollection<string>
    {
        private readonly NamespaceNode _root = new NamespaceNode(null);

        public int Count => this._root.Count(n => n.Ends);

        bool ICollection<string>.IsReadOnly => false;

        public NamespaceDirectoryTree() { }

        public NamespaceDirectoryTree(IEnumerable<string> namespaces)
        {
            foreach (string ns in namespaces)
            {
                this.AddPath(this._root, ns);
            }
        }

        private void AddPath(NamespaceNode node, string ns)
        {
            if (ns.Length == 0)
            {
                throw new ArgumentNullException(nameof(ns));
            }
            NamespaceNode parent = node;
            string parentNamespace = ns;
            int index;
            do
            {
                index = parentNamespace.IndexOf('.');
                if (index == -1)
                {
                    NamespaceNode child = parent.GetOrAddChild(parentNamespace);
                    child.Ends = true;
                }
                else
                {
                    NamespaceNode child = parent.GetOrAddChild(parentNamespace.Substring(0, index));
                    parent = child;
                    parentNamespace = parentNamespace.Substring(index + 1);
                }
            } while (index != -1);
        }
        
        public void Add(string item) => this.AddPath(this._root, item);

        public void Clear() => this._root.Children.Clear();

        public bool Contains(string item) => this._root.EnumerateDirectories().Any(n => n == item);

        bool ICollection<string>.Remove(string item) => throw new NotSupportedException();

        public void CopyTo(string[] array, int arrayIndex)
        {
            foreach (string path in this._root.EnumerateDirectories())
            {
                array[arrayIndex++] = path;
            }
        }

        public IEnumerator<string> GetEnumerator()
        {
            return this._root.EnumerateDirectories().GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();

        private sealed class NamespaceNode : IEnumerable<NamespaceNode>
        {
            public NamespaceNode(string? valueString)
            {
                this.Value = valueString;
                this.Children = new Dictionary<string, NamespaceNode>(BucketStringComparer.Instance);
            }

            public bool Ends { get; set; }

            public string? Value { get; }

            public IDictionary<string, NamespaceNode> Children { get; }

            public NamespaceNode GetOrAddChild(string path)
            {
                if (path is null || path.Length == 0)
                {
                    throw new ArgumentNullException(nameof(path));
                }

                if (this.Children.TryGetValue(path, out NamespaceNode? child))
                {
                    return child;
                }

                child = new NamespaceNode(path);
                this.Children.Add(child.Value!, child);
                return child;
            }

            public IEnumerable<string> EnumerateDirectories()
            {
                if (this.Ends)
                {
                    if (this.Value is null)
                    {
                        yield break;
                    }
                    yield return this.Value;
                }

                string separator = this.Children.Count == 1 && !this.Ends ? "." : "\\";
                foreach (NamespaceNode child in this.Children.Values)
                {
                    foreach (string path in child.EnumerateDirectories())
                    {
                        if (this.Value is null)
                        {
                            yield return path!;
                        }
                        else
                        {
                            yield return String.Concat(this.Value, separator, path)!;
                        }
                    }
                }
            }

            public IEnumerator<NamespaceNode> GetEnumerator()
            {
                var nodeBacklog = new Queue<NamespaceNode>(this.Children.Count);
                nodeBacklog.Enqueue(this);
                while (nodeBacklog.Count != 0)
                {
                    NamespaceNode temp = nodeBacklog.Dequeue();
                    foreach (NamespaceNode child in temp.Children.Values)
                    {
                        nodeBacklog.Enqueue(child);
                    }
                    yield return temp;
                }
            }

            IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
        }
    }
        
    internal sealed class BucketStringComparer : IComparer<string>, IEqualityComparer<string>
    {
        private static readonly Lazy<BucketStringComparer> s_instance = new Lazy<BucketStringComparer>(() => new BucketStringComparer());
        public static BucketStringComparer Instance => s_instance.Value;
        public int Compare( string? x, string? y) => string.Compare(x, y);

        public bool Equals(string? x, string? y) => string.Equals(x, y);

        public int GetHashCode(string obj) => string.IsNullOrEmpty(obj) ? 0 : obj[0] + obj.Length;
    }
}
;