【问题标题】:How to create a tree representing namespaces from their string representations如何从其字符串表示中创建表示名称空间的树
【发布时间】:2015-03-28 17:20:27
【问题描述】:

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

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

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

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

我试图拆分命名空间,但对于我的一生,我想不出一个正确生成它的逻辑。

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

【问题讨论】:

  • namespace Enums.NEWENUMSnamespace Enums.OLDENUMS

标签: c# parsing tree namespaces treeview


【解决方案1】:

1。解析命名空间

这是代表命名空间的类。它将命名空间表示为直接嵌套命名空间的字典。为了从字符串生成Namespaces,它提供了使用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 中的类型并获取它们的所有命名空间:

例如,这段代码获取当前正在执行的Assembly中的所有类型:

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

【讨论】:

  • 非常感谢,就像一个魅力。快速提问,是否可以添加驻留在命名空间中的类型?不能感谢你。再次感谢。
猜你喜欢
  • 2020-05-30
  • 2016-07-19
  • 1970-01-01
  • 2021-11-23
  • 1970-01-01
  • 1970-01-01
  • 2017-08-30
  • 2013-08-17
  • 1970-01-01
相关资源
最近更新 更多