相关链接:
Visual Studio 2010及C# 4.0的预览版资源链接
The Visual Studio 2010 and .NET Framework 4.0 CTP FAQ
呼,终于下决心装上了。我可怜的笔记本。VPC2007的supported system里居然没有Vista Home Premium,装VPC2007SP1时弹出个警告窗口吓了我一跳。后来发觉那警告只是说“不支持”,没说“不让装”或者“不能用”,于是照样装。
那个VPC镜像解压出来之后并不是一开始就有75G那么大。根据FAQ,安装的最小硬盘剩余空间是40G,最好是有75G。分配给虚拟机的内存需要有1024M,我的机器就只有2G内存,初次启动的时候居然失败了,说宿主操作系统没有足够内存 T T……还好关了几个后台服务之后勉强满足了内存需求然后启动了虚拟机。实际运行起来还挺顺畅的。至少开着几个IE都没有卡。说真的用这个虚拟机比用host的Vista舒服……决定了,这段时间开新笔记本的时候就直接进虚拟机了。
现在我就正在这个镜像所附带的Windows Server 2008 Standard上在写这帖。然而刚才看了FAQ之后才发觉不应该那这个虚拟机镜像上网的 OTL
FAQ说这个镜像里的评估版软件给出的激活提示都可以无视,包括Windows Server 2008要求激活的提示。于是刚才我选了忽略,但不知道16天后会怎样呢 -_-
===========================================================================
Anders Hejlsberg在PDC 2008的The Future of C#演讲跟他先前在JAOO2008上的演讲非常相似——前几张演示稿都是一样的。看JAOO的演讲时我超激动,这次则平静了很多……但还是有震惊的部分。直接把JavaScript代码复制到C#源文件里,简单修改后就能直接运行的能力真是有意思,duck typing的演示虽然预见到了但实际看到运行的状况还是很有趣;更有趣的是Anders演示的REPL能力,虽然Mono在这部分已经领先了(
CSharpRepl)。可惜Compiler as a Service的功能到底什么时候能推出,是否能赶上.NET 4/C# 4都还是未知数;多半是要等到C# vNextNext了 -_-|||
Anders一直在说编程语言的趋势有三点:declarative、dynamic、concurrent。而C# 4的主题就是dynamic的部分;concurrent部分则由.NET Framework来提供,等相关功能更加成熟的时候,或许会考虑将这些功能加入到C#的语法中。
===========================================================================
.NET Framework 4.0的更新:
大家特别关注的是PLINQ、CCR等与并行处理相关的内容。这部分……需要花点时间来看,回头再说。DLR相关的部分跟我预期的差不多。
System.dll:
增加了System.Numerics命名空间,里面公开的类型有一个:BigInteger。以前一直有人抱怨微软不在标准库里提供BigInteger的功能,这回从算有了。实际上,这个命名空间并不是新增的,只不过是从3.5里的System.Core.dll中internal的System.Numeric移动过来而已。
System.Core.dll:
许多东西都大幅度更新了,包括System.Linq.Expressions命名空间里新增的语句树的支持,System.Scripting等与脚本语言相关的支持,等等。
有趣的是这个程序集里新增加里Microsoft.CSharp、Microsoft.CSharp.Semantics、Microsoft.CSharp.Syntax等一些非空开命名空间,里面的内容就是……一个完整的C#编译器!Eric Lippert之前提到过他们在用C#来写C#编译器,原来这个工作就是指.NET 4里的C# 4编译器么。有意思。不知道这些API有没有机会暴露更多出来呢。
另外还有一组新增的API也相当有趣:System.Shell.CommandLine命名空间。提供了十分方便的命令行解析的功能。
===========================================================================
C# 4.0的一些小实验
刚才在C# Future的论坛看到NyaRuRu发的一帖:
Ambiguity with variant generics/
Variance Ambiguity,于是写了相似的代码用这次CTP里的C# 4.0编译器测了一下:
using System;
using System.Text.RegularExpressions;
public interface ICountable<out T> {
int Count { get; }
}
public class MyCollection : ICountable<string>, ICountable<Regex>, ICountable<Match> {
int ICountable<Regex>.Count {
get { return 1; }
}
int ICountable<string>.Count {
get { return 2; }
}
int ICountable<Match>.Count {
get { return 3; }
}
}
public static class Program {
static void Main( string[ ] args ) {
var col = new MyCollection( );
ICountable<object> countable = col;
Console.WriteLine( countable.Count );
}
}
编译能通过,运行结果是2。一开始让人挺摸不着脑的,再试了一下发现这里实际选取了哪个版本的ICountable<out T>.Count取决于MyCollection声明其实现的接口的顺序。如果把ICountable<Regex>写在第一位的话,运行结果就会是1。这真是太诡异了,然而这并不是C# 4自身就能解决的问题,而是涉及到CLR的类型系统。
这段代码在C# 3或以前的版本里自然是编译不了。如果把ICountable<out T>的out去掉并在Main的代码里做强制类型转换(ICountable<object>)(object)col,那么在C# 3可以编译通过,但在.NET Framework 3.5上运行会得到运行时异常:
System.InvalidCastException,无法将类型为“MyCollection”的对象强制转换为类型“ICountable<object>”。
看了看生成出来的MSIL,除了ICountable<out T>写为ICountable<+T>外,其它都跟C# 3编译出来的一样。差异就在这里了。把C# 3编译出来的版本用ildasm解成MSIL,手工添加上variance标记(那个加号),再用ilasm编译回exe,就能看到.NET Framework 3.5上也能运行该代码并得到与C# 4版本同样的结果。事实上variance的功能在CLR里一早就存在了,只是C#在4之前一直没暴露这个功能而已。MSR的Andrew Kennedy写过一篇
相关的论文,不知道最终.NET 4会不会选择修改类型系统来处理这个问题呢?还是说C# 4的编译器会做些处理?只能等了。
然后看看C# 4的动态特性。借助
C# Future的
IDynamicObject Example里实现的Dynamic类,可以写出这样的代码:
using System;
using System.Collections.Generic;
using System.Scripting.Actions;
class NameValuePair {
public string Name { get; set; }
public object Value { get; set; }
}
// a custom dynamic lookup implementation
class PropertyBag : Dynamic {
Dictionary<string, object> _items;
public PropertyBag( ) {
_items = new Dictionary<string, object>( );
}
public override object GetMember( GetMemberAction action ) {
return _items[ action.Name ];
}
public override void SetMember( SetMemberAction action, object value ) {
_items[ action.Name ] = value;
}
}
class DuckTyping {
static void Main( string[ ] args ) {
// plain C# object, statically typed
var pair = new NameValuePair {
Name = "Plain Old C# Object",
Value = "The Value"
};
PrintNameValue( pair );
// custom dynamic object, statically typed to be "dyanmic"
dynamic props = new PropertyBag( );
props.Name = "Property Bag Instance";
props.Value = "The Value Property";
PrintNameValue( props );
// anonymous type object
var anoTypeObj = new {
Name = "Anonymous Type Instance",
Value = "Another Value"
};
PrintNameValue( anoTypeObj );
}
// duck typing
static void PrintNameValue( dynamic obj ) {
Console.WriteLine( "Name: {0}", obj.Name );
Console.WriteLine( "Value: {0}", obj.Value );
}
}
可以看到PrintNameValue()这个方法的参数被声明为dynamic类型的,于是这个方法实际上就用到了Python、Ruby社区里流行的duck typing:只要一个对象有名为Name和Value的成员,PrintNameValue()就能应用到该对象上,无论是普通的.NET类型(NameValuePair)、匿名类型、还是实现了IDynamicObject的“动态类型”(PropertyBag),都没问题。
(Anders和Jim做的演示里都用到了System.Dynamic.DynamicObject类,但CTP里这个类并不存在。怪哉,CTP还是不够新啊。还好这个类可以从C# Future获得,也可以直接从DLR源码中获得(IronRuby里,System.Scripting.Actions中的Dynamic类))
然而这次的CTP里的C#编译器似乎并没有对CallSite对象的创建做多少优化,生成的代码看起来还有很大的改进空间。想了一下,刚才觉得可以优化的一个地方看来是不适合做静态优化,还是像现在这样每个调用点都创建一个CallSite对象来得好些。
使用了动态类型的方法调用有一个非常非常重要的特性,很可能会被人忽略:方法的分发从单一分发(single-dispatch)变成了多分发(multi-dispatch)。举例来说,假如有这么一组类:
public class A { }
public class B : A { }
public class Foo {
public virtual void Bar( A a ) { }
public virtual void Bar( B b ) { }
}
public class Goo : Foo {
public override void Bar( A a ) { }
public override void Bar( B b ) { }
}
那么使用dynamic与否就会带来区别:
class Program {
static void Main( string[ ] args ) {
// plain old single dispatch
Foo goo = new Goo( );
A b = new B( );
goo.Bar( b ); // calls Goo.Bar( A )
// multi-dispatch
dynamic b1 = b;
goo.Bar( b1 ); // calls Goo.Bar( B )
}
}
这个例子里两次对Bar()的调用都用的是Goo上的版本体现出了方法分发的效果。
单一分发:方法调用会根据第一个参数的实际类型(而不是变量声明的类型)来决定选用的版本。
多分发:所有参数的实际类型都是分发的判断条件。
原本在C#里,成员方法都有一个隐式参数this作为第一个参数,而分发也是针对this来进行的。现在有了动态类型支持,动态方法调用的分发就变成了多分发。
有了这个特性,在实现Visitor模式的时候会方便很多。可以关注一下这个特性以后的使用状况。