如何从字符串表示形式创建表示名称空间的树

How to create a tree representing namespaces from their string representations

如何为命名空间创建树状数据结构。

例如,对于这些命名空间:

Enums.NEWENUMS.NEW1
Enums.NEWENUMS.NEW2
Enums.NEWENUMS.NEW3
Enums.OLDENUMS
Enums.TEST.SUB
Enums.TEST.SUB.OK

然后将其加载到树视图中,如下所示:

我尝试拆分名称空间,但我一直想不出正确生成它的逻辑。

还尝试按照您生成目录结构的方式来生成它,但由于命名空间需要拆分,我无法理解它。

1。解析命名空间

这里是代表命名空间的class。它将命名空间表示为直接嵌套命名空间的字典。为了从字符串生成 Namespaces,它提供了使用 recursive calls and LINQ:

的静态方法
public class Namespace : IDictionary<String, Namespace>
{
    #region Static

    public static IEnumerable<Namespace> FromStrings(IEnumerable<String> namespaceStrings)
    {
        // Split all strings
        var splitSubNamespaces = namespaceStrings
            .Select(fullNamespace =>
                fullNamespace.Split('.'));

        return FromSplitStrings(null, splitSubNamespaces);
    }

    public static IEnumerable<Namespace> FromSplitStrings(Namespace root, IEnumerable<IEnumerable<String>> splitSubNamespaces)
    {
        if (splitSubNamespaces == null)
            throw new ArgumentNullException("splitSubNamespaces");

        return splitSubNamespaces
            // Remove those split sequences that have no elements
            .Where(splitSubNamespace =>
                splitSubNamespace.Any())
            // Group by the outermost namespace
            .GroupBy(splitNamespace =>
                 splitNamespace.First())
            // Create Namespace for each group and prepare sequences that represent nested namespaces
            .Select(group =>
                new
                {
                    Root = new Namespace(group.Key, root),
                    SplitSubnamespaces = group
                        .Select(splitNamespace =>
                            splitNamespace.Skip(1))
                })
            // Select nested namespaces with recursive split call
            .Select(obj =>
                new
                {
                    Root = obj.Root,
                    SubNamespaces = FromSplitStrings(obj.Root, obj.SplitSubnamespaces)
                })
            // Select only uppermost level namespaces to return
            .Select(obj =>
                obj.Root)
            // To avoid deferred execution problems when recursive function may not be able to create nested namespaces
            .ToArray(); 
    }

    #endregion



    #region Fields

    private IDictionary<String, Namespace> subNamespaces;

    #endregion


    #region Constructors

    private Namespace(String nameOnLevel, Namespace parent)
    {
        if (String.IsNullOrWhiteSpace(nameOnLevel))
            throw new ArgumentException("nameOfLevel");

        this.Parent = parent;
        this.NameOnLevel = nameOnLevel;
        this.subNamespaces = new Dictionary<String, Namespace>();

        if (this.Parent != null)
        {
            this.Parent.Add(this.NameOnLevel, this);
        }
    }

    private Namespace(String nameOfLevel)
        : this(nameOfLevel, null)
    {

    }

    #endregion


    #region Properties

    public String NameOnLevel
    {
        get;
        private set;
    }

    public String FullName
    {
        get
        {
            if (this.Parent == null)
                return this.NameOnLevel;

            return String.Format("{0}.{1}",
                this.Parent.FullName,
                this.NameOnLevel);
        }
    }

    private Namespace _Parent;

    public Namespace Parent
    {
        get
        {
            return this._Parent;
        }
        private set
        {
            if (this.Parent != null)
                this.Parent.Remove(this.NameOnLevel);

            this._Parent = value;
        }
    }

    #endregion



    #region IDictionary implementation

    public void Add(string key, Namespace value)
    {
        if (this.ContainsKey(key))
            throw new InvalidOperationException("Namespace already contains namespace with such name on level");

        this.subNamespaces.Add(key, value);
    }

    public bool ContainsKey(string key)
    {
        return this.subNamespaces.ContainsKey(key);
    }

    public ICollection<string> Keys
    {
        get { return this.subNamespaces.Keys; }
    }

    public bool Remove(string key)
    {
        if (!this.ContainsKey(key))
            throw new KeyNotFoundException();

        this[key]._Parent = null;

        return this.subNamespaces.Remove(key);
    }

    public bool TryGetValue(string key, out Namespace value)
    {
        return this.subNamespaces.TryGetValue(key, out value);
    }

    public ICollection<Namespace> Values
    {
        get { return this.subNamespaces.Values; }
    }

    public ICollection<Namespace> Subnamespaces
    {
        get { return this.subNamespaces.Values; }
    }

    public Namespace this[string nameOnLevel]
    {
        get
        {
            return this.subNamespaces[nameOnLevel];
        }
        set
        {
            if (value == null)
                throw new ArgumentException("value");

            Namespace toReplace;

            if (this.TryGetValue(nameOnLevel, out toReplace))
            {
                toReplace.Parent = null;
            }

            value.Parent = this;
        }
    }

    public void Add(KeyValuePair<string, Namespace> item)
    {
        this.Add(item.Key, item.Value);
    }

    public void Clear()
    {
        foreach (var subNamespace in this.subNamespaces.Select(kv => kv.Value))
        {
            subNamespace._Parent = null;
        }

        this.subNamespaces.Clear();
    }

    public bool Contains(KeyValuePair<string, Namespace> item)
    {
        return this.subNamespaces.Contains(item);
    }

    public void CopyTo(KeyValuePair<string, Namespace>[] array, int arrayIndex)
    {
        this.subNamespaces.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return this.subNamespaces.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(KeyValuePair<string, Namespace> item)
    {
        return this.subNamespaces.Remove(item);
    }

    public IEnumerator<KeyValuePair<string, Namespace>> GetEnumerator()
    {
        return this.subNamespaces.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    #endregion



    #region Overrides

    public override string ToString()
    {
        return this.FullName;
    }

    #endregion
}

P.S:这个class可能有几个错误实现的方法。

P.S.1: 无需LINQ即可重写解析方法。事实上,这个 LINQ 解决方案不是很地道,也不是一个很好的例子来说明如何以及何时使用 LINQ。但它很短而且很简单。

2。将命名空间添加到 TreeView

你没有提到你使用的UI框架,所以我默认了Windows表单。假设您已将名为 treeView_Namespaces 的 TreeView 添加到表单中:

public Form1()
{
    InitializeComponent();

    var namespaceStrings = new String[]
    {
        "Enums.NEWENUMS.NEW1",
        "Enums.NEWENUMS.NEW2",
        "Enums.NEWENUMS.NEW3",
        "Enums.OLDENUMS",
        "Enums.TEST.SUB",
        "Enums.TEST.SUB.OK"
    };

    var namespaces = Namespace.FromStrings(namespaceStrings);

    AddNamespaces(this.treeView_Namespaces.Nodes, namespaces);
}

void AddNamespaces(TreeNodeCollection nodeCollection, IEnumerable<Namespace> namespaces)
{
    foreach (var aNamespace in namespaces)
    {
        TreeNode node = new TreeNode(aNamespace.NameOnLevel);
        nodeCollection.Add(node);

        AddNamespaces(node.Nodes, aNamespace.Subnamespaces);
        node.Expand();
    }
}

3。如果你需要从真正的命名空间生成这样的树

为此,您必须遍历 Assembly 中的类型并获取它们的所有命名空间:

例如,此代码获取当前正在执行的程序集中的所有类型:

var namespaceStrings = Assembly
    .GetExecutingAssembly()
    .GetTypes()
    .Select(type =>
        type.Namespace)
    .Where(@namespace =>
        @namespace != null);