65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 3.0 特性

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.48/5 (34投票s)

2007年12月20日

CPOL

7分钟阅读

viewsIcon

108213

downloadIcon

211

展示 C# 3.0 的新语言特性

引言

C# 语言变得越来越美观。C# 3.0 就是美的定义。新的 C# 编译器为我们做了大量工作,让我们来看看如何开始使用这些美丽的特性。

C# 3.0 特性列表

让我们从一个非常方便的特性开始,“自动属性”(我不确定这个名字是否准确,但我喜欢这样称呼它 :))。基本上,您可以在 C# 中声明一个属性,而无需显式创建数据成员,编译器会代劳创建它。

C# 2.0
private string prop;
public string Prop
{
get { return prop; }
set { prop = value; }
} 
C# 3.0
public string Prop { get; set; } 

正如您所见,我没有声明成员;您只需指定对属性的要求(是否只读)。

对象初始化器

在 C# 2.0 中,每当您需要构造一个对象并设置其初始状态时,我们过去会重载构造函数并通过构造函数传递属性值。C# 3.0 通过对象初始化器引入了一种新的方式来实现这一点。

所以,如果我有这个类

class MyClass
{
private string prop;
public string Prop
{
get { return prop; }
set { prop = value; }
}
} 
C# 2.0
MyClass c = new MyClass();
c.Prop = "asdads"; 
C# 3.0
MyClass c = new MyClass { Prop = "test" }; 

集合初始化器

集合初始化器类似于对象初始化器,但它们允许您在集合中添加项。

C# 2.0
List<string> list = new List<string>(2);
list.Add("test1");
list.Add("test2"); 
C# 3.0
List<string> list = new List<string>{"test1", "test2"}; 

字典初始化器

顾名思义,这是一种新的添加项到 IDictionary 的方式。

C# 2.0
IDictionary<string, string> dict = new Dictionary<string, string>(2);
dict.Add("test1", "test1");
dict.Add("test2", "test2"); 
C# 3.0
IDictionary<string, string> dict = new Dictionary<string, string> 
	{ { "key1", "value1" }, { "key2", "value2" } };

扩展方法

这是一个非常酷的新特性。基本上,您可以在编译时将方法附加到特定类型。例如,假设您想向 string 类型添加一个方法。string 类位于 System.dll 中,您无法访问其源代码。您可以声明一个扩展方法来扩展特定类型(您也可以为接口创建扩展方法,并且实现该接口的每个类都将拥有该方法)。扩展方法必须声明为 static 方法,并包含在 static 类中。您必须在方法的参数前面加上 "this"(参数的类型必须是您要扩展的类型)。让我们试试吧。

static class StringExtensions
{
public static string AppendMyNameToString(this string value)
{
return value + " Marlon";
}
} 

现在我可以编写以下代码

Console.WriteLine("Hello".AppendMyNameToString()); 

结果将是

"Hello Marlon" 

LINQ

我不会详细介绍这个主题,因为它是一个很大的话题。如果您有兴趣,请访问 此 URL 获取有关 LINQ 的更多信息。基本上,LINQ 是一种声明式查询数据的方式。所以,而不是使用命令式的方式、循环和 if 语句,您可以告诉 C# 您想要什么,LINQ 会为您构建数据。举个例子,您有一个 string 列表,并且您想获取所有以字母 "s" 开头的 string

string[] list = new string[] { "test", "marlon", "ema" }; 
C# 2.0
List<string> results = new List<string>();
foreach (string str in list)
if(str.StartsWith("t"))
results.Add(str); 
C# 3.0
IEnumerable<string> result = from x in list
where x.StartsWith("t")
select x; 

上面的代码等同于

IEnumerable<string> result5 = list.Where(a => a.StartsWith("t")).Select(a => a);

WhereSelect 方法是 IEnumerable<T> 接口的扩展方法,它们声明在 Enumerable static 类中。a => a.StartWith("t") 语法是 lambda 函数。我们将在下一节解释这个新特性。

正如您所见,LINQ 是一种声明式查询数据的方式。代码更具可读性。我再次强调,我不会深入 LINQ 的细节,但这并不意味着 LINQ 仅限于这个简单的例子,相信我,LINQ 是巨大的。我们还将在文章稍后使用 orderbygroupjoins 进行一些更高级的查询。所以,请保持期待…… :)

匿名类型和隐式类型变量

匿名类型基本上是您在代码中没有声明为类的类型。当您创建匿名类型时,编译器会为您创建一个类(带有非常奇怪的名字 :))。所以您可能会说,如果我不知道类型的名字,我怎么引用它并创建该类型的变量呢?答案是隐式类型变量,也称为varvar 是 C# 中的一个新关键字,编译器会将其替换为类型名称。所以是的,您不会失去静态类型!您也可以在其他场景中使用 var 来在声明变量时输入更少的字符。匿名类型在使用 LINQ 查询以从查询中获取数据结构时非常有用(甚至在讨论 LINQ 中的分组时也很有用)。所以,让我们创建一个匿名类型。

var x = new { Property1 = "hello", Property2 = 5 };
Console.WriteLine(x.Property1);
Console.WriteLine(x.Property2.ToString());

现在让我们在 LINQ 查询中使用匿名类型来看看它的潜力

var result3 = from q in testList
where q.TestProperty.StartsWith("T")
select new { Text = q.TestProperty, InnerDataList = q.ListOfData };


foreach (var item in result3)
Console.WriteLine("Text = {0}, Inner List Count = {1}", 
item.Text, item.InnerDataList.Count); 

很酷吧.. :)

Lambda 表达式

在 C# 2.0 中,匿名方法诞生了。这是一个很酷的特性,因为如果您只需要使用一次某个方法(例如事件处理程序或 Predicate<T>),您就不必在类中创建它。此外,您可以使用(有一些限制,但今天我不谈论)匿名方法声明上下文中的任何变量。

C# 2.0
List<string> listOfstrings = new List<string>();
.....
listOfstrings.ForEach(delegate(string s) 
{ 
s.Trim(); 
}); 
C# 3.0
listOfstrings.ForEach(str => str.Trim());

如上例所示,lambda 语法更简洁。基本上,通过 lambda,您可以通过说 x=> 来指定您想要传递的参数,现在您可以对 x 做任何事情,因为 x 是传递给您的参数。如果您需要将多个变量传递给 lambda,您可以通过说 (x, y)=> 来做到,如果您需要在主体中执行多个代码行,您可以使用 { } 来完成。

部分方法

C# 3.0 引入了部分方法。基本上,这是一种在部分类中定义方法并在另一个部分类中实现它的方法。这个特性对设计师更有用。例如,LINQ to SQL 设计器大量使用这些特性。

partial class Customer
{
public partial void TestPartial();
}

partial class Customer
{
partial void TestPartial()
{
...
}
}

所以我想这些就是 C# 3.0 中的新特性(据我所知!)。我没有对每个特性进行详细介绍,因为每个特性都值得一篇单独的文章。然而,正如承诺的那样,我将深入研究一些 LINQ。我再次只介绍基本内容,不深入细节。我只展示如何使用这些特性。

使用 LINQ 进行排序

您可以使用 LINQ 非常轻松地对集合进行排序

List<int> intList = new List<int>{15,22,1,14,25,96,3,8,91};
var sortQuery = from z in intList
orderby z ascending
select z; 

正如您所见,orderby 关键字强制对集合进行排序。(排序功能需要由 LINQ 提供程序实现。LINQ to Objects 的排序已为所有集合实现)。

LINQ 中的分组

分组是 LINQ to Objects 的另一个很酷的特性(我说 LINQ to Objects,因为有不止一种 LINQ 提供程序。有 LINQ to XML、LINQ to SQL 等……但我在这篇文章中只使用 LINQ to Objects)。例如,想象一下您有一组数字,并且您想检查哪个数字除以 2 有余数。

var groupQuery = from z in intList
group z by z % 2 == 0 into g
select new { Key = g.Key, Numbers = g };
foreach (var item in groupQuery)
Console.WriteLine("Reminder of 2: {0}, Count: {1}", item.Key, item.Numbers.Count());

正如您所见,这里我使用了一个匿名类型来创建一个组。g.Key 将是分组谓词(z % 2 == 0)的不同结果。

LINQ 中的连接 (Joins)

当您拥有关系数据时,连接 (Joins) 非常强大。例如,想象一下您有一组客户和这些客户的订单列表。您想通过汇总客户的所有订单来找出客户的总账单。您可以使用 LINQ 非常轻松地做到这一点。

让我们准备一些样本数据

Customer cust1 = new Customer { CustumerId = 1 };
Customer cust2 = new Customer { CustumerId = 2 };
List<Customer> customers = new List<Customer> { cust1, cust2 };
List<CustomerOrder> orders = new List<CustomerOrder>
{
new CustomerOrder { Customer = cust1, Total = 90 }, 
new CustomerOrder { Customer = cust1, Total = 190 }, 
new CustomerOrder { Customer = cust2, Total = 10 }
}; 

现在让我们查询这些数据以通过它们的关系连接两个列表。

var joinQuery = from cust in customers
join order in orders
on cust.CustumerId equals order.Customer.CustumerId
into customerOrders
select new { CustomerId = cust.CustumerId, Total = 
	customerOrders.Sum(custOrder => custOrder.Total) }; 

让我们打印结果……

foreach (var item in joinQuery)
Console.WriteLine("Customer {0} has a total of {1}", item.CustomerId, item.Total);

结论

C# 3.0 确实强大,让我们的生活变得更轻松(尤其是有了 LINQ)。当您将所有这些很酷的特性结合在一起时,您会得到一种非常巧妙的方式来实现简洁、可读且易于理解的代码。我再次想指出,我没有深入细节,因为文章会太长。希望您喜欢……

历史

  • 2007年12月20日:初始发布
© . All rights reserved.