5.F# vs C# 网页下载

此博客文章停止维护,访问个人博客链接

在下面的例子中,我们将比较F#和C#用代码来实现网页的下载,其利用回调函数来处理文本流。
让我们直接看F#的实现
// "open" 声明.net的命名空间,使命名空间在例子可见                                                    open System.Netopen Systemopen System.IO

// 获取网页的内容                                                                                 let fetchUrl callback url =        
    let req = WebRequest.Create(Uri(url)) 
    use resp = req.GetResponse() 
    use stream = resp.GetResponseStream() 
    use reader = new IO.StreamReader(stream) 
    callback reader url

让我们一起来走读下代码:

  • 在顶部使用"open"让我们可以使用"WebRequest"而不是"System.Net.WebRequest",他就像使用 "using System.Net" 在C#顶部。
  • 接下来,我们定义了函数 fetchUrl ,它接受两个参数,一个实处理文本流的毁掉函数,一个是获取网页的url地址.
  • 接下来我们封装了url字符到一个Uri对象中. F# 拥有严格的类型检查, 如果我们用下面这几替代之前写的( let req = WebRequest.Create(url) )编译器将会提示,他不知道应该使用哪个版本的WebRequest.Create.
  • 在定义 responsestream 和reader 值时, 我们使用了哦 "use"关键字代替"let".他只被用在链接实现了IDisposable接口的类.它告诉编译器会自动处理资源当它出了作用域后。这就像C#中使用C# "using" 关键字.
  • 最后一行掉用了代StreamReader和url参数的回调函数。注意这个回调类型并没有制定类型现在让我们看看相对应的C#实现.
class WebPageDownloader                                                                     {
    public TResult FetchUrl<TResult>(
        string url,
        Func<string, StreamReader, TResult> callback)
    {
        var req = WebRequest.Create(url);
        using (var resp = req.GetResponse())
        {
            using (var stream = resp.GetResponseStream())
            {
                using (var reader = new StreamReader(stream))
                {
                    return callback(url, reader);
                }
            }
        }
    }                                                                                       }

就像之前一样,C#的代码有很多“噪声”。

  • 其中有10函只是纯粹的花括号。
  • 有5个层级的缩进增加了视觉的复制性。
  • 所有的参数类型都必须显示的定义,即使是泛型的 TResult 类型也被重复了三次。

*是的,在一些特殊的例子中,有些括号和缩进可以被删除,但是大部分的情况下他们还是必须的

测试代码

回到F#我们可以直接在交互式窗口进行测试

let myCallback (reader:IO.StreamReader) url = 
    let html = reader.ReadToEnd()
    let html1000 = html.Substring(0,1000)
    printfn "Downloaded %s. First 1000 is %s" url html1000
    html      // 返回所有html

//测试                                                                                      let google = fetchUrl myCallback "http://google.com"
最后我们必须对reader参数进行类型定义 
( reader:IO.StreamReader). 这是必须的因为F#编译器不能自动的判定“reader”的类型。
在F#中有一个很有用的特性, 你可以“bake in”(个人觉得有点像C#的缺省默认参数,但是又有不同)参数到一个函数,那样他就不用每次都传递参数。这就是为什么   url  参数放在最后一个而不是第一个 , 在C#代码中,一旦回调函数设置了,url变量就从调用者传递到被调用者.

// 构建一个函数将回调函数“baked in"                                                            let fetchUrl2 = fetchUrl myCallback 

// 测试                                                                                      let google = fetchUrl2 "http://www.google.com"                                      let bbc    = fetchUrl2 "http://news.bbc.co.uk"

// 测试的网站序列                                                                              let sites = ["http://www.bing.com";
             "http://www.google.com";
             "http://www.yahoo.com"]

// 处理序列中的每一网站                                                                        sites |> List.map fetchUrl2

最后一行 (使用 List.map)他让我们可以利用一个新的函数很容易结合list处理函数,一次性的下载整个list的网页

下面是相对应的C#测试代码:

       
       
[Test]public void TestFetchUrlWithCallback(){
    Func<string, StreamReader, string> myCallback = (url, reader) =>
    {
        var html = reader.ReadToEnd();
        var html1000 = html.Substring(0, 1000);
        Console.WriteLine(
            "Downloaded {0}. First 1000 is {1}", url,
            html1000);
        return html;
    };

    var downloader = new WebPageDownloader();
    var google = downloader.FetchUrl("http://www.google.com",
                                      myCallback);
            
    // 测试的网站序列
    var sites = new List<string> {
        "http://www.bing.com",
        "http://www.google.com",
        "http://www.yahoo.com"};

    // 处理序列中的每个网站
    sites.ForEach(site => downloader.FetchUrl(site, myCallback));}
在此会发现,C#代码相比F#有更多的“噪声”,许多现实的类型引用,更重要的是C#代码不能很容易的让你bake in参数到函数中。因此回调函数必须每次都显示的引用。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值