改进我选择具有唯一值的多个 XElements 来构建列表的方法

Improve on my way of selecting multiple XElements with unique values to build list

有没有办法改进这个?我觉得我有办法进行许多循环。

该代码从 XML 文档构建一个包含域名的字符串。根据 XML 文档中的域名是否在 hostSW(host Starts With)、hostCN(host Contains)或 hostEW(host EndsWith)元素中取决于我是否需要在末尾附加 *,开始+结束,或值的开始。

我使用哈希集来确保没有重复。

var startWith = xdoc.Root
    .Descendants("Test")
    .Elements("hostSW")
    .ToList();
var contains = xdoc.Root
    .Descendants("Test")
    .Elements("hostCN")
    .ToList();
var endsWith = xdoc.Root
    .Descendants("Test")
    .Elements("hostEW")
    .ToList();

HashSet<string> domains = new HashSet<string>(); //use hashset so we don't duplicate results

foreach (XElement test in startWith)
{
    domains.Add(test.Value.ToString() + "*");
}
foreach (XElement test in contains)
{
    domains.Add("*" + test.Value.ToString() + "*");
}
foreach (XElement test in endsWith)
{
    domains.Add("*" + test.Value.ToString());
}

string out = "BEGIN FILE ";
foreach (string domain in domains.ToArray())
{
    out += "BEGIN DOMAIN ";
    out += domain;
    out += " END DOMAIN";
}
out += " END FILE;

return out;

XML 文件

<Tests>
    <Test>
        <hostSW>startsWith1</hostSW>
    </Test>
    <Test>
        <hostSW>startsWith2</hostSW>
    </Test>
    <Test>
        <hostCN>contains1</hostCN>
    </Test>
    <Test>
        <hostEW>endsWith1</hostEW>
    </Test>
</Tests>

假设 XML 结构像这样

<Root>
    <Test>
        <hostSW>startsWith1</hostSW>
    </Test>
    <Test>
        <hostCN>contains1</hostCN>
    </Test>
    <Test>
        <hostCN>contains2</hostCN>
    </Test>
    <Test>
        <hostEW>endsWith1</hostEW>
    </Test>
    <Test>
        <hostEW>endsWith2</hostEW>
    </Test>
    <Test>
        <hostEW>endsWith3</hostEW>
    </Test>
</Root>

我正确地得到了所有域,并且只用这段代码迭代了 6 次

// Loop each of the elements one time without filtering element name
foreach (var test in xDoc.Root.Descendants("Test").Elements())
{
    // Switch on the name of the element.
    switch (test.Name.LocalName)
    {
        case "hostSW": { domains.Add(test.Value + "*"); } break;
        case "hostCN": { domains.Add("*" + test.Value + "*"); } break;
        case "hostEW": { domains.Add("*" + test.Value); } break;
    }
    // For any other elements we iterate just do nothing
}

使用 Linq 来 XML 你可以:

var t = xml.Root.Descendants("Test")
    .Elements()
    .Where(x => x.Name == "hostSW" || x.Name == "hostCN" || x.Name == "hostEW")
    .Select(x => (x.Name == "hostSW") ? 
                       $"{x.Value}*" 
                       : 
                       (x.Name == "hostCN" ? 
                           $"*{x.Value}*" 
                           :
                           $"*{x.Value}"));

如果您的 <Test> 元素只包含 <hostSW><hostCN><hostEW>,那么您可以省略 Where

Demo here

您尝试过使用 SelectSingleNode or the SelectNodes method with the XmlNode class 吗?我知道您正在尝试避免循环,但对于这种情况,只要我们知道根(父节点)保持不变,您就应该收到 O(n) 的时间复杂度。

您的 XML 文件似乎包含包含您需要的内部文本的节点,因此您可以这样做:

foreach (XmlNode node in xmlDoc.SelectNodes($"//Test/Test"))
{
    switch (node.Name)
    {
        case "hostSW":
            var newInnerText = node.InnerText + '*';
            //do something
            break;
        case "hostCN":
            var newInnerText = node.InnerText + '*';
            //do something
            break;
        case "hostEW":
            //do something
            break;
        default:
            //do something
            break;
    }
}

这是另一种方法。这使用 LINQ Select 而不是循环,并使用 Dictionary 而不是 switch / 三叉树。它还利用了替代机制:在本例中我使用了 string.Format,但包括 string.Replace 在内的许多其他机制同样适用。这完全取决于您要做什么。我通常追求合理的性能和经济的语法,通常在易于维护方面得分很高。

static Dictionary<string, string> dict = new Dictionary<string, string>() {
    { "hostSW", "{0}*" },
    { "hostCN", "*{0}*" },
    { "hostEW", "*{0}" },
};

static IEnumerable<string> WildcardStringsFromXML(XDocument xdoc)
{
    return xdoc.Root.Descendants("Test").Elements()
               .Select(item => string.Format(dict[item.Name.LocalName], item.Value));
}