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

时间:2015-03-28 17:20:27

标签: c# parsing tree namespaces treeview

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

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

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

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

enter image description here

我试图拆分名称空间,但是对于我的生活,我无法想到正确生成它的逻辑。

还尝试以生成目录结构的方式生成它,但由于名称空间需要拆分而无法理解它。

1 个答案:

答案 0 :(得分:3)

1。解析命名空间

这是表示命名空间的类。它将名称空间表示为直接嵌套名称空间的字典。要从字符串生成Namespace,它会提供使用recursive callsLINQ的静态方法:

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