最近阅读F#,结果是“async”和“!” (Bang!:p)是非常有用的功能。 (关于主题,这是编程语言不仅是学术研究课题,而且是现实生产力设计主题的一个例子。)
以下是Luca Bolognese的PDC 2008演示文稿:
let internal loadPrices ticker = async {
let url = "http://ichart.finance.yahoo.com/table.csv?s=" + ticker + "&d=9&e=30&f=2008&g=d&a=2&b=13&c=1986&ignore=.csv"
let req = WebRequest.Create(url)
let! resp = req.AsyncGetResponse() //athos: need F# Powerpack
let stream = resp.GetResponseStream()
let reader = new StreamReader(stream)
let! csv = reader.AsyncReadToEnd()
let prices =
csv.Split([|'\n'|])
|> Seq.skip 1
|> Seq.map (fun line -> line.Split([|','|]))
|> Seq.filter (fun values -> values |> Seq.length = 7)
|> Seq.map (fun values ->
System.DateTime.Parse(values.[0]),
float values.[6])
return prices }
迷人的部分是,优雅地处理回调:程序员不需要处理BackgroundWorker或Theads的Join()。
所以,在C#中,有一个优雅的解决方案,实现F#“aync”和“!”特征? 例如,是否可以按照以下方式或以类似的轻松方式执行“async ... run ... callback”?
public void GetApplicationSettings()
{
async(objAsyncRunner = new System.Async.Runner() ) //similar to "using" in C#
run // similar to "if" in C#
{
string url = "http://ichart.finance.yahoo.com/table.csv?s=" + ticker + "&d=9&e=30&f=2008&g=d&a=2&b=13&c=1986&ignore=.csv"
var req = WebRequest.Create(url);
var resp = req.AsyncGetResponse();
}
callback run // similar to "if else" in C#
{
var stream = resp.GetResponseStream();
var reader = new StreamReader(stream);
var csv = reader.AsyncReadToEnd();
}
callback // similar to "else" in C#
{
var prices = csv.Split('\n').....;
}
}
PS。我知道有些人会说:athos,只需使用F#! 是的...我不反对F#但是需要时间来改变整个部门,在此之前我希望借用F#的一些优点。
我知道有一些Aspect编程概念可以扩展C#功能。但是它仍然没有达到F#水平。
例如,我可以从http://www.sharpcrafters.com扩展PostSharp,以便通过简单地为方法分配属性,强制它以异步方式在专用线程中运行:
[RunInADedicatedThread(Async = true)]
public void GetApplicationSettings()
{ ... }
如果准备了属性:
[Serializable]
[AttributeUsage( AttributeTargets.Method | AttributeTargets.Class )]
[MulticastAttributeUsage( MulticastTargets.Method )]
public sealed class RunInADedicatedThreadAttribute : MethodInterceptionAspect
// MethodInterceptionAspect is extending PostSharp
{
public bool Async { get; set; }
public override void OnInvoke( MethodInterceptionArgs args )
{
if (Async)
{
//MyThreadPool is my extention for PostSharp
MyThreadPool.GetThread().ExecuteAsync(
() =>
{
args.Proceed();
} );
return;
}
MyThreadPool.GetThread().Execute( args.Proceed );
}
}
这有点帮助,但它仍然不是F#级别:
- “在专用线程中运行,异步”请求没有在调用上定义,而是在方法GetApplicationSettings()本身上定义!这个方法也可以重命名为GetApplicationSettingsAsynchronously(),不是吗;
- 另外,这对回调部分无能为力;
- 更不用说F#“async”功能使用更少的线程...我现在甚至可以放弃这个“advaced”功能......
答案 0 :(得分:4)
正如布莱恩已经提到的那样,Visual Studio Async CTP是你正在寻找的。它为未来版本的C#添加了类似异步工作流程的功能。它添加了async
方法,就像F#中的异步工作流和await
关键字一样,行为类似于let!
。两者之间存在一些差异,我写了series of articles that compares them。
不幸的是,C#中的异步支持仍然是CTP。如果要编写生产质量的异步代码,最好的选择是在F#中编写它并使用Tasks
从C#引用它。这SO question discusses how to do that。
您可以通过各种方式在C#中模拟async
之类的内容。 Brian提到使用LINQ(在他的博客文章中),但这非常有限。我认为使用迭代器更灵活。以下是article that does exactly that的示例:
static IEnumerable<IAsync> DownloadAsync(string url)
{
WebRequest req = HttpWebRequest.Create(url);
Async<WebResponse> response = req.GetResponseAsync();
yield return response;
Stream resp = response.Result.GetResponseStream();
Async<string> html = resp.ReadToEndAsync().ExecuteAsync<string>();
yield return html;
Console.WriteLine(html.Result);
}
这与async workflows
类似,因为C#迭代器也被编译为状态机。杰弗里里希特AsyncEnumerator
implements a more complete API基于这个想法。
答案 1 :(得分:2)
见
http://msdn.microsoft.com/en-us/vstudio/async.aspx
和
获取有用信息。第一个链接是用于C#和VB的“异步”CTP(将在这些语言的未来版本中出现),第二个链接显示了今天利用LINQ在C#中执行异步的方法。
答案 2 :(得分:2)
这是C#中使用FSharpx的相同功能,它看起来与F#版本几乎相同:
FSharpAsync<IEnumerable<Tuple<DateTime, decimal>>> LoadPrices(string ticker) {
var url = "http://ichart.finance.yahoo.com/table.csv?s=" + ticker + "&d=9&e=30&f=2008&g=d&a=2&b=13&c=1986&ignore=.csv";
var req = WebRequest.Create(url);
return
from resp in req.FSharpAsyncGetResponse()
let stream = resp.GetResponseStream()
let reader = new StreamReader(stream)
from csv in reader.FSharpAsyncReadToEnd()
select (from line in csv.Split('\n').Skip(1)
let values = line.Split(',')
where values.Length == 7
select Tuple.Create(DateTime.Parse(values[0]), decimal.Parse(values[6], CultureInfo.InvariantCulture)));
}
样本用法:
var price = LoadPrices("MSFT").Run().First();
Assert.AreEqual(new DateTime(2008,10,30), price.Item1);
Assert.AreEqual(20.82m, price.Item2);
这里的关键概念是LINQ不仅仅是语言集成的查询:它的语言集成了 monads 。 FSharpx只是在C#/ VB.NET和it's pretty trivial code中实现从LINQ访问F#async所需的绑定。
FSharpx需要.NET 3.5或更高版本,但原则上你可以在C#3,.NET 2.0上使用它。
其中一个缺点是LINQ中没有use!
(原来应该在原来的F#代码中使用),所以管理一次性用品是有问题的。