C# 3.0

 1, Implicitly typed local variables(隐式类型的局部变量)引入 var 关键字,容许变量类型从代码上下文中引出:
var n = 10;
var f = 10.0;
var a = new int [] {1,2,3,4};
int [] ns = {1,2,3,4,5};
foreach(var v in ns)
当然C# 仍然是强类型的,如果发现类型无法判断,那么还是会编译时报错,var s = null; // 编译错误,因为无法根据null知道 s 是什么类型这个没什么好解释的,是个人都知道这是什么意思
2. Extension Method (扩展方法)
容许对存在的类型添加方法(相当于2.0中的partial class)但是现在即使你原来没有申明 partial 属性, 这样的类你也可以给它添加方法了,下面是例子:
首先要新建一个静态的类, 给它添加一些静态的方法但是最重要的是第一个参数是 this 开头namespace Acme.Utilities
{
    public static class Extensions
    {
        public static int ToInt32(this string s) {
            return Int32.Parse(s);
        }
        public static T[] Slice<T>(this T[] source, int index, int count) {
            if (index < 0 || count < 0 || source.Length - index < count)
                throw new ArgumentException();
            T[] result = new T[count];
            Array.Copy(source, index, result, 0, count);
            return result;
        }
    }
}
这样当你使用时, using Acme.Utilities;
这时这个包中所有的"扩展方法"都会被合并到当前的类型中比方,对于 string 类型的, 就相当于存在一个 String.ToInt32() 这样一个方法了而对于 T[] 类型的,就存在一个 T[].Slice(int index, int count) 这样的方法(注意这里 T[] 是一个模版类型,如果类型有条件不知道该怎么声明了)
using Acme.Utilities;
string s = "1234";
int i = s.ToInt32(); // 相当于调用了 Same as Extensions.ToInt32(s)
int[] digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] a = digits.Slice(4, 3); // 相当于调用了 Extensions.Slice(digits, 4, 3)
3.Lambda Expression(λ表达式) 引入了 => 操作符
C# 2.0 中引入了匿名函数,容许内嵌写委托函数, 但是还是稍嫌复杂而 λ表达式 可以让这个"使用"(请注意是使用)变得简洁得多,它的语法基本如下 : (参数) => 表达式, 或者 参数 => 表达式,具体大家可以看 那篇文档中的语法. 下面是例子
(int x) => { return x + 1; }
(x, y) => x * y
() => Console.WriteLine()
看实际应用:
namespace System.Query
{
    public static class Sequence
    {
        public static IEnumerable<S> Select<T,S>(
            this IEnumerable<T> source,
            Func<T,S> selector)
        {
            foreach (T element in source) yield return selector(element);
        }
    }
}
这里定义了一个扩展方法,给 IEnumerable<T>类型 加了一个 Select 方法这个方法需要一个 delegate Func<T, S> selector 以便对 IEnumerable<T> 中每个元素都执行 Func<T,S>() 操作, 并且返回了 Func<T,S>() 这个操作的结果如果你比较熟悉 STL ,那么这个和其中的 for_each 算法是比较相像的现在看怎么调用这个方法:
List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);
这样我们看到 c => c.Name 规则 就变成了 delegate Func<T,S> selector
从而 selector 就对应于 string select(Customer c) { return c.Name; }
也就是对每个 Customer, 取得他的 Name 值关于上面的代码综合运用了 "隐式类型", "模版方法" "扩展方法", 和 (λ表达式)如果你看不懂属于正常, 其实 (λ表达式) 真正强在于能自动根据代码上下文匹配到对应的模版方法, 再来看一个例子:
static Z F<X,Y,Z>(X value, Func<X,Y> f1, Func<Y,Z> f2) {
    return f2(f1(value));
}
double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalSeconds);
从上面, C# 编译器能自动知道 "1:15:30" 是 string类型,返回值是 double 类型于是 Z 对应 double, X 对应 string, 而 s => TimeSpan.Parse(s) 可以知道 s 是 string 类型, 返回 TimeSpan于是 Func<X, Y> f1 应当是函数 TimeSpan f1(string s); X 对应 string, 于是 Y 对应TimeSpan同理,由t => t.TotalSeconds 知道 double f2(TimeSpan), Y 对应 TimeSpan, Z 对应double所以当 使用 double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalSeconds);时,那个 F 函数就被翻译成了static double F(string value,
TimeSpan f1(string s){ return TimeSpan.Parse(s); },
double f2(TimeSpan) { return TimeSpan.TotalSeconds; } );看到了吧, 极其强大, 使用也极其简洁, 但后面隐藏的规则也极其复杂如果真有这样复杂的函数,我不知道会有多少人能够理解 :)这里我跳过了一些关于函数重载时,(λ表达式)的内部如何匹配的处理规则,请看原文
4.Object and collection initializer(对象和集合的初始化代码块)就是让你在 new 对象时,可以同时初始化对象的一些属性,看看例子就明白了,先看对象的初始化
ublic class Point
{
    int x, y;
    public int X { get { return x; } set { x = value; } }
    public int Y { get { return y; } set { y = value; } }
}
var a = new Point { X = 0, Y = 1 }; // 后面{}里的内容就是我们说的初始化代码块这个和先创建 Point, 然后分别设置 X,Y属性是一样的,就是稍微简洁了点var a = new Point(); a.X = 0; a.Y = 1;真正重要的是集合的初始化代码块, 因为你要知道,在以前C# 1/2 是无法在初始化一个集合/数组时初始化好每个元素的属性的(如果这些属性不被包括在构造函数中的话)但是现在有了对象的初始化代码块, 现在就可以了, 例子:
List<Point> line = new List<Point>
{ new Point {X = 1,Y =1}, new Point {X = 10, Y=10} };
而这个在以前是无法完成的, 除非 Point 有一个构造函数 Point(int x, int y);
5.Anonymous Types(匿名类型)
靠,C#越来越变得脚本化了, 现在你不用再像以前那样先写好一个class,然后再来用了, 而是直接把class的代码写在对象的初始化代码块中, 看例子:
var p1 = new { Name = "Lawnmower", Price = 495.00 };
上面等于定义了一个类型, 具有 Name, Price 成员字段目前好像只能支持 Field / Property,无法在里面加方法当然如果你定义了两个相同的匿名类型, C# 还是会比较智能的合并的
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;
6.Implicitly typed arrays(隐式类型数组)
就是创建数组时让编译器自动判断数组类型
var a = new []{1,2,3,4}; // 这个就相当于 int [] a = new int[]{1,2,3,4};
var b = new []{"r", "g", "b"}; // 相当于创建了一个字符串数组
7.Query Expression(查询表达式)
重要的来了 :), 这个应当是 C# 集成了那个 LINQ项目的结果,不过一路看下来,发现前面所有的工作都是为了这个目的如果有谁写过大型机上的数据库程序,估计对代码中嵌入SQL都不会太陌生,比方 SQL-C不过MS让这个又更进了一步,让QUERY同时支持了XML数据和SQL查询以及表达式并且和 代码无缝集成, 果然有够强具体语法还是请大家看原文,大致的文法如下:from 标志符 in 表达式(,标志符 in 表达式(,...)) where 条件 orderby 排序表达式 (group 表达式 by 表达式) 或者 (select 表达式)into 标志符基本上和 SQL 语句一样, 只是 select 语句在后面但是真正有意思的是C# 本身是不处理这个查询表达式如何执行而是把它转换成了对每个表达式Where,Select,SelectMany,OrderBy,GroupBy等这样的方法的调用, 比方你写了
from c in customers where c.City = "London" select c.Name那么对于这里的where 和 select 语句的处理就被转换成了函数调用customers.Where(c => c.City = "London").Select(c => c.Name);这样前面提到的 λ表达式 和 扩展方法 就发挥作用了.只要你的customers 对象存在扩展方法 Where,或者一般的方法Where并且Where返回的结果支持方法 Select, 那么这个查询就可以执行而根本不在乎你的customers是XML还是数据库的表, 而且由于可以使用 扩展方法 , 你只要到时候添加一个实现了 Where,Select这些扩展方法的函数包,就可以让需要的数据类型支持这种 查询表达式再看一些其他例子:
from c in customers group c.Name by c.Country
转换成 customers.GroupBy(c => c.Country, c=>c.Name);from c in customers orderby c.Name select new {c.Name, c.Phone};转换成 customers.OrderBy(c => c.Name).Select(c => new {c.Name, c.Phone});
再看一个嵌套的查询
from c in customers where c.City == "London"
from o in c.Orders where o.OrderDate.Year == 2005
select new { c.Name, o.OrderID, o.Total }
转换成
customers.
Where(c => c.City == "London").
SelectMany(c =>
c.Orders.
Where(o => o.OrderDate.Year == 2005).
Select(o => new { c.Name, o.OrderID, o.Total })
);
可以预见,到时候 .Net Framework 必定会附带一个 System.Query.dll
里面实现了绝大部分Collection, Xml, Database这些对象的
Where/Select 这些 扩展方法 以支持查询表达式
8.Expression trees(表达式树)
这个原文中没有做过多的介绍,里面说还有一篇文档是专门介绍这个主题的基本大意是对于 λ表达式
Func<int,int> f = x => x + 1; // Code可以使用 System.Query中的一个模版类 Expression<D> 把表达式转成数据Expression<Func<int,int>> e = x => x + 1; // Data也就是对于 f 这个其实是一个可以运行的代码,执行 x = x+1;
对于 e, 则是一个描述代码 f 的一个数据结构!

posted on 2006-04-20 21:29  kim  阅读(614)  评论(0编辑  收藏  举报

导航