c#do功能相当吗?

时间:2016-06-06 14:42:36

标签: c# functional-programming do-while

我正在编写一个控制台应用程序来进行Web服务调用并接收XML,并且该XML将是“文件夹”或“文档”的条目。我在C#中编写了以下do while循环来循环调用webservice,但我想知道是否有更多功能的方法来执行它:

 ...
        var documentUrls = new Dictionary<string, string>();

        // get the root folder's children
        var rootFeed = $"{baseUrl}/biprws/infostore/{23}/children"
            .Do(str => Console.WriteLine($"{str}:"))
            .GetResponse(headerToken)
            .GetXmlString()
            .DeserializeFromXml<Library.Children.Feed>();

        var folderUrls = rootFeed.Entries
            .Where(entry => entry.Content.ContentContainsFolderUrl(type: "folder"))
            .Select(entry => $"{entry.Link.Href}/children")
            .ToList();

        // go through each folder and pull out the documents - if a nested folder is found then check that too
        do
        {
            var feeds = folderUrls
                .Select(url => url.GetResponse(headerToken).GetXmlString().DeserializeFromXml<Library.Children.Feed>())
                .SelectMany(x => x.Entries);

            var folders = feeds
                .Where(entry => entry.Content.ContentContainsFolderUrl(type: "Folder"))
                .Select(x => $"{x.Link.Href}/children")
                .ToList();

            var documents = feeds
                .Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"))
                .ToDictionary(
                    keySelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "id").First().Text,
                    elementSelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "name").First().Text);

            // if there are no more folders or documents to check, break out of this while loop
            if (!documents.Any() && !folders.Any())
                break;

            Console.WriteLine($"Got {folders.Count} folders and {documents.Count} documents");

            folderUrls.RemoveAll(x => true); // remove all the folder urls as they have already been checked!!!!
            folderUrls.AddRange(folders);
            documents.ForEachDo(x => documentUrls.Add(x.Key, x.Value));
        } while (true);

        Console.WriteLine($"There are {documentUrls.Count()} {nameof(documentUrls)}");
...

3 个答案:

答案 0 :(得分:1)

没有无限循环的模式通常会在循环之前预先加载退出条件,

    var folders = feeds
            .Where(entry => entry.Content.ContentContainsFolderUrl(type: "Folder"))
            .Select(x => $"{x.Link.Href}/children")
            .ToList();

    var documents = feeds
            .Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"))
            .ToDictionary(
                keySelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "id").First().Text,
                elementSelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "name").First().Text);

然后使用while循环条件作为退出测试:

while(documents.Any() || folders.Any()){

        var feeds = folderUrls
            .Select(url => url.GetResponse(headerToken).GetXmlString().DeserializeFromXml<Library.Children.Feed>())
            .SelectMany(x => x.Entries);

        Console.WriteLine($"Got {folders.Count} folders and {documents.Count} documents");

        folderUrls.RemoveAll(x => true); // remove all the folder urls as they have already been checked!!!!
        folderUrls.AddRange(folders);
        documents.ForEachDo(x => documentUrls.Add(x.Key, x.Value));

最后在循环之前加载退出条件的变量:

        folders = feeds
            .Where(entry => entry.Content.ContentContainsFolderUrl(type: "Folder"))
            .Select(x => $"{x.Link.Href}/children")
            .ToList();

        documents = feeds
            .Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"))
            .ToDictionary(
                keySelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "id").First().Text,
                elementSelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "name").First().Text);

}

...

这种方法重复代码,因此为每个查询创建方法,因此重复的代码只是对适当方法的几次调用。这将导致代码更易读,因为代码中的描述性方法名称比LINQ查询更容易理解(更不用说它不会有无限循环)。这种模式可能需要更多的努力来处理变量范围,但在这种情况下,这应该不是问题(在这种情况下feed应该是方法的唯一参数)。

答案 1 :(得分:1)

使用forwhile或递归,这不是更多功能。这主要是为了避免副作用。

E.g。如何使用folderUrls跟踪已经检查和嵌套的文件夹不是很有用。相反,设计一个函数,它获取文件夹列表并返回文档。即定义明确的输入和输出。从您的代码示例中,很难说出您想要实现的目标。

这个函数可以像这样解释:

// I don't know what the type of "entry" is. Replace "TEntry" with the correct type.
public IEnumerable<TEntry> LoadDocuments(IEnumerable<TEntry> feedEntries)
{
    var folderUrls = feedEntries
        .Where(entry => entry.Content.ContentContainsFolderUrl(type: "folder"))
        .Select(entry => $"{entry.Link.Href}/children");

    if (!folderUrls.Any())
        return Enumerable.Empty<TEntry>();

    var feeds = folderUrls
        .Select(url => url.GetResponse(headerToken)
                          .GetXmlString()
                          .DeserializeFromXml<Library.Children.Feed>())
        .SelectMany(x => x.Entries)
        .ToList();

    // Recursive call to load nested documents.    
    var nestedDocuments = LoadDocuments(feeds);

    var documents = feeds
        // What's the type of "entry" here? Use that type for the return type of this 
        // function.
        .Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"));

    return documents.Concat(nestedDocuments);
}

调用它并像以前一样将此函数的输出转换为字典:

var rootFeed = $"{baseUrl}/biprws/infostore/{23}/children"
    .Do(str => Console.WriteLine($"{str}:"))
    .GetResponse(headerToken)
    .GetXmlString()
    .DeserializeFromXml<Library.Children.Feed>();

var documentUrls = LoadDocuments(rootFeed.Entries)
    .ToDictionary(
        x => x.Content.Attrs.Attr
              .Where(y => y.Name.ToLower() == "id")
              .First().Text,
        x => x.Content.Attrs.Attr
              .Where(y => y.Name.ToLower() == "name")
              .First().Text);

答案 2 :(得分:0)

关心自己的典型化

static void Main(string[] args)
{
    var documentUrls = new Dictionary<string, string>();

    Feed rootFeed = $"{baseUrl}/biprws/infostore/{23}/children"
        .Do(str => Console.WriteLine($"{str}:"))
        .GetResponse(headerToken)
        .GetXmlString()
        .DeserializeFromXml<Library.Children.Feed>();

    IEnumerable<object> folderUrls = rootFeed.Entries
        .Where(entry => entry.Content.ContentContainsFolderUrl(type: "folder"))
        .Select(entry => $"{entry.Link.Href}/children")
        .ToList();

    object feeds;
    object folders;
    object documents;

    Init(folderUrls, out feeds, out folders, out documents);

    while (documents.Any() || folders.Any())
    {
        Console.WriteLine($"Got {folders.Count} folders and {documents.Count} documents");

        folderUrls.RemoveAll(x => true);
        folderUrls.AddRange(folders);
        documents.ForEachDo(x => documentUrls.Add(x.Key, x.Value));

        Init(folderUrls, out feeds, out folders, out documents);
    }

    Console.WriteLine($"There are {documentUrls.Count()} {nameof(documentUrls)}");

}

private static void Init(object foldersUrl, out object feeds, out object folders, out object documents)
{
    feeds = InitFeeds(foldersUrl);
    folders = InitFolders(feeds);
    documents = InitDocs(feeds);
}

private static object InitDocs(object feeds)
{
    var documents = feeds
        .Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"))
        .ToDictionary(
            keySelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "id").First().Text,
            elementSelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "name").First().Text);
    return documents;
}

private static object InitFolders(object feeds)
{
    var folders = feeds
        .Where(entry => entry.Content.ContentContainsFolderUrl(type: "Folder"))
        .Select(x => $"{x.Link.Href}/children")
        .ToList();
    return folders;
}

private static object InitFeeds(object folderUrls)
{
    var feeds = folderUrls
        .Select(url => url.GetResponse(headerToken).GetXmlString().DeserializeFromXml<Feed>())
        .SelectMany(x => x.Entries);
    return feeds;
}