C# 3.0 特性






4.48/5 (34投票s)
展示 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);
Where
和 Select
方法是 IEnumerable<T>
接口的扩展方法,它们声明在 Enumerable static
类中。a => a.StartWith("t")
语法是 lambda 函数。我们将在下一节解释这个新特性。
正如您所见,LINQ 是一种声明式查询数据的方式。代码更具可读性。我再次强调,我不会深入 LINQ 的细节,但这并不意味着 LINQ 仅限于这个简单的例子,相信我,LINQ 是巨大的。我们还将在文章稍后使用 orderby
、group
和 joins
进行一些更高级的查询。所以,请保持期待…… :)
匿名类型和隐式类型变量
匿名类型基本上是您在代码中没有声明为类的类型。当您创建匿名类型时,编译器会为您创建一个类(带有非常奇怪的名字 :))。所以您可能会说,如果我不知道类型的名字,我怎么引用它并创建该类型的变量呢?答案是隐式类型变量,也称为var。var
是 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日:初始发布