c# - 如何根据字符串表示形式创建表示命名空间的树

标签 c# parsing tree namespaces treeview

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

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

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

然后将其加载到 TreeView 中,如下所示:

enter image description here

我尝试分割命名空间,但我一生都想不出正确生成它的逻辑。

还尝试以生成目录结构的方式生成它,但无法理解它,因为 namespace 需要拆分。

最佳答案

1。解析命名空间

这是代表命名空间的类。它将命名空间表示为直接嵌套命名空间的字典。为了从字符串生成命名空间,它提供了使用 recursive calls 的静态方法。和 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:此类可能有一些错误实现的方法。

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

关于c# - 如何根据字符串表示形式创建表示命名空间的树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29320175/

相关文章:

iOS Parse Block 结果存储在 UILabel 中

c - 在二叉树中添加节点时使用指向结构指针的指针

c# - 如何在C#中为组合框的文本设置图像

c# - 如何上传文件到sharepoint文档库

c# - (DataGridView + Binding)如何根据绑定(bind)的对象给线条上色?

c# - 如何使用反射对 Entity Framework 模型的属性执行 ToString

javascript - 使用 xPath(javascript, jquery) 从 url 链接解析 html 页面

php - 使用 php jsonpath 解析 JSON

c# - WCF 对象序列化问题

java - 树遍历和一阶函数