通过引用的对象,将不允许我将引用交换为新对象

时间:2013-12-03 22:07:17

标签: c# object

好的,我会尝试尽可能地解释我的问题,从下面的代码片段我将tempURLTestedVsCaptured传递给我的方法CheckForDuplicates_capturedUrls。

此方法将检查是否存在重复项,如果没有,则会将URL(包含在我的URL对象中)添加到新的URL对象中。完成后,它会将原始tempURL设置为新对象的引用。

我有tempURLTestedVsCaptured的问题是没有得到新的引用。如果我观察tempURL,它在方法结束时具有正确的值,当它跳回到Crawl方法时,tempURLTestedVsCaptured已返回到原始值。

如果我更改了tempURL,例如添加了一个URL,则会反映出这些更改。

如果我这样做:

tempURLs = new URLs();
tempURLs = processedURLs;

它不会接受改变。在我的学习中,我显然遗漏了一些非常有趣的东西,但我不能指责它。

private void CheckForDuplicates_capturedUrls(URLs tempURLs)
        {
            URLs unprocessedURLs = (URLs)tempURLs;
            URLs processedURLs = new URLs();

                foreach (URL url in unprocessedURLs)
                {
                    if (!crawlContext.capturedUrls.ContainsURL(url))
                    {
                        processedURLs.AddURL(url);
                    }


                }
                    tempURLs = new URLs();
                    tempURLs = processedURLs;  
        }


        private void Crawl(WebScraper_Context crawlContext)
        {
            URLs tempURLTestedVsVisited = new URLs();
            URLs tempURLTestedVsCaptured = new URLs();

            while (crawlContext.unVistedURLs.Count() != 0) //While we have URLS we not visited, continue
            {
                foreach (URL url in crawlContext.unVistedURLs)
                {
                    //  If we not visted the page yet
                    if (!crawlContext.vistedURLs.ContainsURL(url))      //  Vist the URL if there is one
                    {
                        crawlContext.vistedURLs.AddURL(url);
                        LoadPage(url.url);                  
                        doc = GetSubSetXPath(doc, crawlContext.xPath);         
                    }                                                                       
                    if (doc != null)
                    {
                        crawlContext.scrapedUrls = ScrapeURLS();                                        
                        crawlContext.scrapedUrls = GetLocalUrls(crawlContext.scrapedUrls);              

                        // Cache the URLS into, so we can check if we seen them before
                        foreach (URL newURL in crawlContext.scrapedUrls)
                        {
                            if (!tempURLTestedVsVisited.ContainsURL(newURL))
                            {
                                tempURLTestedVsVisited.AddURL(newURL);
                                tempURLTestedVsCaptured.AddURL(newURL);
                            }
                            else
                            {
                                System.Windows.Forms.MessageBox.Show("Duplicate URL found in scraped URLS");
                            }
                        }


                        **this.CheckForDuplicates_capturedUrls(tempURLTestedVsCaptured);**



                        foreach (URL newURL in crawlContext.scrapedUrls)
                        {
                            if (tempURLTestedVsVisited.ContainsURL(newURL) && tempURLTestedVsCaptured.ContainsURL(newURL))
                            {
                                crawlContext.newURLs.AddURL(newURL);
                                crawlContext.capturedUrls.AddURL(newURL);

                            }
                        }



                    }
                }

                crawlContext.unVistedURLs = new URLs();                                                  crawlContext.unVistedURLs = crawlContext.newURLs;
                crawlContext.newURLs = new URLs();

            }
            if (RequestStop == true)
            {
                RequestStop = false;
            }
            System.Windows.Forms.MessageBox.Show("Complete");
        }

Ok T. Kiley完全解释了我的问题以及我为什么会这样做。 我没有返回URLS的原因,我正在做一个毫无意义的演员,因为方法签名计划是:

private void CheckForDuplicates_capturedUrls(object tempURLs).

该方法将用作线程启动“DuplicateCheckerB = new Thread(this.CheckForDuplicates_capturedUrls);”和“DuplicateCheckerA.Start(tempURLTestedVsVisited);”我最初认为我的问题归结为线程,所以我剥离了它在调试过程中。

现在,如果我要将其传递给线程,我是否会认为我必须修改实际对象以删除URL?

5 个答案:

答案 0 :(得分:3)

您没有通过引用传递方法参数:

private void CheckForDuplicates_capturedUrls(ref URLs tempURLs) {...}

并称之为:

CheckForDuplicates_capturedUrls(ref tempURLTestedVsCaptured);

或者, 最好 ,只需返回一个新列表:

private URLs CheckForDuplicates_capturedUrls(URLs tempURLs) {
    URLs result = new URLs();
    // process tempURLs, storing the result
    return result;
}

并像这样使用它:

tempURLTestedVsCaptured = CheckForDuplicates_capturedUrls(tempURLTestedVsCaptured);

答案 1 :(得分:2)

您的代码存在一些问题,但是首先需要了解第一种方法的问题,首先需要了解传递引用和传递值之间的区别。这个概念在C#和Java(比C ++ [imo])这样的语言中更加混乱,因为它看起来像是通过引用传递,实际上它(几乎)总是通过值传递。

基本上,当函数通过值时,参数的值将复制到函数中。显然,如果你这样做,任何修改都将丢失。

另一方面,通过引用传递,只是告诉函数在哪里查看,因此将保留修改。

C#按值传递,但您传递的值是指针

这在实践中意味着,你将指针传递给你的tempUrls。如果你要修改这个指针指向的东西,你会修改你传入的东西。

但是,你在方法中做了什么:

tempURLs = processedURLs;

积分会变成新事物。我们通过值传递地址,因此更改地址将不会执行任何操作。来电者仍在寻找旧位置。

您可以使用ref关键字解决此问题,但在此方法中,返回新的tempUrls可能会更好

其他问题 您的代码还有其他一些问题:

  • 如果类型匹配,则无需强制转换
  • 当你在tempUrls上调用new()时,你会指向一些新的内存。下面的一行,你把它指向processedUrls,所以新的完全是多余的。如果您确实需要对象
  • ,则只需使用new

答案 2 :(得分:1)

在C#中,参数是按值传递的。这意味着您在所调用的方法中获得了引用的副本。您对该副本所做的任何更改都不会影响该方法调用之外的其他引用。通过对参数使用ref关键字,您可以对原始引用进行更改,但不要这样做。从方法中返回新值是更好的方法。

private URLs CheckForDuplicates_capturedUrls(URLs tempURLs) {...}

答案 3 :(得分:1)

您可以使用引用参数修复此问题,但更好的设计是按照以下模式返回新列表:

tempUrls = CheckForDuplicates_capturedUrls(URLs tempURLs)

private URLs CheckForDuplicates_capturedUrls(URLs tempURLs)
{
    //It's not clear what options your URLs type has, but based on
    // your example use, it looks like you might be inheriting List<url>
    // or implementing IList. This code makes that assumption

    return tempURLs.Where(url => !crawlContext.capturedUrls.ContainsURL(url)).ToList();
}

我们甚至将代码缩减为单行代码:)

原始代码不起作用,因为.Net按值传递对函数的引用。这意味着该函数获取引用同一对象的引用,但仍然只是一个副本。该函数可以更改对象中的属性,或调用它的方法,这些更改对于函数的调用者来说是显而易见的。但是,如果函数尝试直接对变量本身进行赋值,则只会更改副本。

答案 4 :(得分:-3)

在C#中,对象通过引用传递。但是,当您提供一个方法对象时,它会生成指针的副本(引用是一个安全指针),然后将其提供给该函数。

将'ref'添加到方法调用中,您将传递原始引用。

private void CheckForDuplicates_capturedUrls( ref 网址tempURLs)