匿名类型使用关键字var
。简而言之,匿名类型就是说你不用指定具体的类型。只要写上var
即可,CSharp
将计算出右侧表达式所定义的数据类型。然后CSharp
编译器指定所定义的变量为该类型。类型被指定以后就相当于强类型,且由编译器在运行时进行类型检查
。
请脊柱,你无需编写类型定义,因为CSharp会帮你计算出来。这一点很重要,因为在查询语言中,你所请求的以及所获取到的任何特定类型都是根据上下文(查询结果)来定义的。简单地说,查询结果可能会返回一个之前没有定义的类型。
1. 使用匿名类型时需要遵守的一些基本规则
- 匿名类型必须有一个初始值,而且这个值不能是空值(null),因为类型是根据初始化器推断出来的
- 匿名类型可以用于简单类型,也可以用于复杂类型。不过,用于定义简单类型时,其价值不大
- 复合匿名类型需要有成员声明。比如说
var joe = new {Name="Joe"}
- 匿名类型支持只能感知技术
- 匿名类型不能用于类的字段
- 匿名类型可以在
for
训话中用作初始化器 - 可以使用
new
关键字;数组的初始化器必须使用new
关键字 - 匿名类型可以用于数组
- 所有匿名类型都派生于
Object
类型 - 方法可以返回会匿名类型,不过需要转转成
object
,这破坏了强类型原则 - 初始化匿名类型时可以加上方法,不过可能只有所谓的语言学家才会对这种做法产生兴趣
2. 使用匿名类型
2.1 定义简单匿名类型
var title = "LINQ Unleashed for C#";
2.2 使用数组初始化器语法
var fibonacci = new int[]{ 1, 1, 2, 3, 5, 8, 13, 21 };
2.3 创建复核匿名类型
var dena = new {First="Dena", Last="Swanson"};
2.4 给匿名类型添加方法
该技术定义了一个匿名委托,并将该匿名委托赋值给泛型类Func。在该示例中,Concat被定义为一个匿名委托,他接受两个字符串,将他们拼接到一起后再返回一个新的字符串。你可以将这个委托赋值给任何一个被定义为该Func实例(拥有三个子字符串类型的形参)的变量。最后,再在该匿名类型定义中将变量Concat赋值给一个成员声明器。
Func<string, string, string> Concat = delegate (string first, string last)
{
return last + ", " + first;
};
var dena = new { First = "Dena", Last = "Swanson", Concat = Concat };
Console.WriteLine(dena.Concat(dena.First, dena.Last));
另外,匿名委托中亦可以使用反射机制获取信息:
Func<Type, Object, string> Concat = delegate (Type t, Object o)
{
var info = t.GetProperties();
return (string)info[1].GetValue(o, null) + ", " + (string)info[0].GetValue(o, null);
};
var dena = new { First="Dena", Last="Swanson", Concat=Concat };
Console.WriteLine(dena.Concat(dena.GetType(), dena));
2.5 在For循环中使用匿名类型索引
var fibonacci = new int[] { 1, 1, 2, 3, 5, 8, 13, 21 };
for (var i = 0; i < fibonacci.Length; i++)
Console.WriteLine(fibonacci[i]);
2.6 在Foreach中使用匿名类型索引
var fibonacci = new int[] { 1, 1, 2, 3, 5, 8, 13, 21 };
foreach (var fibo in fibonacci)
Console.WriteLine(fibo);
由于要在Foreach语句中迭代一个对象,唯一需要满足的条件就是该对象必须实现了IEnumerable或IEnumberable。这也是可绑定性(比如GridView的数据绑定)的前提条件。以下的例子中,LINQ查询语句和拓展方法均返回了一个可枚举的对象。
var fibonacci = new int[] { 1, 1, 2, 3, 5, 8, 13, 21, 33, 54, 87 };
// uses Lambda expression with Where<int, bool> and Where is an extension method for IEnumerable
foreach (var fibo in fibonacci.Where(n => n % 3 == 0))
Console.WriteLine(fibo);
// uses LINQ query
foreach (var fibo in from f in fibonacci where f % 3 == 0 select f)
Console.WriteLine(fibo);
2.7 匿名类型和Using语句
using语句是try…finally的简洁表示。使用try…finally或using的目的是,确保资源在using块退出或finally块结束之前被释放。通过调用Dispose(它要求在using语句中创建的那个对象必须实现了IDisposable)即可实现该目的。
string connectionString = "Data Source=BUTLER;Initial Catalog=AdventureWorks2000;Integrated Security=True";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
Console.WriteLine(connection.State);
}
2.8 从函数返回匿名类型
由于垃圾回收器会清理任何对象,因此,匿名各类型是可以从函数返回的。不过在定义范围之外,匿名类型指示一个object的实例而已。不幸的是,返回一个object将会使只能感知技术五熊,而且还会批灰匿名类型的强类型特性。虽然你可以通过反射来重新找出该匿名类型的功能,不过这也将使得这个本来很舒服的东西变得不那么舒服了。
static void Main(string[] args)
{
var anon = GetAnonymous();
var t = anon.GetType();
Console.WriteLine(t.GetProperty("Stock").GetValue(anon, null));
}
public static object GetAnonymous()
{
var stock = new { Stock = "MSFT", Price = "32.450" };
return stock;
}
2.9 匿名类型的数据绑定
var quote1 = new { Stock = "DELL", Quote = GetQuote("DELL") };
var quote2 = new { Stock = "MSFT", Quote = GetQuote("MSFT") };
var quote3 = new { Stock = "GOOG", Quote = GetQuote("GOOG") };
var quotes = new object[] { quote1, quote2, quote3 };
DataList1.DataSource = quotes;
DataList1.DataBind();
2.10 测试匿名类型的相等性
匿名类型的相等性
定义得非常严格。如果两个匿名类型有用相同的成员声明顺序、成员变量、成员投影类型以及成员名称,则认为他们是相等的
。这种情况下是可以使用引用相等运算符的。如果想要测试成员的相等性,可以使用Equal方法。拥有相同的顺序/类型和名称/类型的匿名类型,器成员声明器的值也会产生相同的哈希值;而这些哈希值就是相等性测试的基础。
var audioBook1 = new { Artist = "Bob Dylan", Song = "I Shall Be Released" }; // Label1
var audioBook2 = new { Song = "I Shall Be Released", Artist = "Bob Dylan" }; // Label2
var songBook1 = new { Artist = "Bob Dylan", Song = "I Shall Be Released" }; // Label3
var songBook2 = new { Singer = "Bob Dylan", Song = "I Shall Be Released" }; // Label4
Console.WriteLine("====================变量类型====================");
Console.WriteLine("audioBook Type: " + audioBook1.GetType().ToString());
Console.WriteLine("audioBook Type: " + audioBook2.GetType().ToString());
Console.WriteLine("audioBook Type: " + songBook1.GetType().ToString());
Console.WriteLine("audioBook Type: " + songBook2.GetType().ToString());
Console.WriteLine("====================哈希代码====================");
Console.WriteLine("audioBook1 hash " + audioBook1.GetHashCode());
Console.WriteLine("audioBook2 hash " + audioBook2.GetHashCode());
Console.WriteLine("songBook1 hash " + songBook1.GetHashCode());
Console.WriteLine("songBook2 hash " + songBook2.GetHashCode());
Console.WriteLine(audioBook1 == songBook1);
Console.WriteLine(audioBook1.Equals(songBook1));
Console.WriteLine(audioBook1.Equals(songBook2));
Console.WriteLine(songBook1.Equals(songBook2));
Console.WriteLine(audioBook2.Equals(audioBook1));
2.11 通过LINQ查询使用匿名类型
var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var all = from n in numbers orderby n descending select n;
foreach (var n in all)
Console.WriteLine(n);
var songs = new string[] { "Let it be", "I shall be released" };
var newType = from song in songs select new { Title = song };
foreach (var s in newType)
Console.WriteLine(s.Title);
2.12 泛型匿名方法简介
除了没有名字之外,匿名方法跟普通方法是一样的。它是通过定义委托来完成简单任务的一种方案,而完整的方法则意味着更多的代码量。匿名方法哈发展出了Lambda表达式。
static void Main(string[] args)
{
Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);
Console.CancelKeyPress += delegate
{
Console.WriteLine("Anonymous Cancel pressed");
};
Console.ReadLine();
}
static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
Console.WriteLine("Cancel pressed");
}
2.13 使用匿名泛型方法
System.Func<long, long> Factorial = delegate (long n)
{
if (n == 1)
return 1;
long result = 1;
for (int i = 2; i <= n; i++)
result *= i;
return result;
};
Console.WriteLine(Factorial(6));
2.14 实现内嵌的递归
上例中,使用For循环的执行计算的。下例中将该段代码改成使用递归的方式。最大的困难在于,这个命名委托载器定义完成之前是没有名字的。因此,在该匿名委托内部是不能使用这个名字的,不过你还是可以完成这个任务。
有一个叫StackFrame的类。StackFrame允许从调用栈中获取方法,也就是说,你可以利用这个类和反射来递归调用匿名委托。
Func<long, long> Factorial = delegate (long n)
{
return n > 1 ? n * (long)(new StackTrace().GetFrame(0).GetMethod().Invoke(null, new object[] { n - 1 })) : n;
};
Console.WriteLine(Factorial(6));