【发布时间】:2016-12-20 19:58:03
【问题描述】:
我了解匿名类型没有自己的预定义类型。类型是由编译器在编译类型时分配给它的,在编译时分配的类型的详细信息在代码级别是未知的;这些细节是 CLR 自己知道的。我听说 CLR 中的这些匿名类型被视为仅作为引用类型。所以我的问题是,是否在编译时创建一个新的类型,如类或结构,对应于匿名类型中定义的只读属性?
【问题讨论】:
标签: c# anonymous-types
我了解匿名类型没有自己的预定义类型。类型是由编译器在编译类型时分配给它的,在编译时分配的类型的详细信息在代码级别是未知的;这些细节是 CLR 自己知道的。我听说 CLR 中的这些匿名类型被视为仅作为引用类型。所以我的问题是,是否在编译时创建一个新的类型,如类或结构,对应于匿名类型中定义的只读属性?
【问题讨论】:
标签: c# anonymous-types
我了解匿名类型没有自己的预定义类型。
正确。除了匿名类型共有的对象之外,没有其他基本类型。
类型是由编译器在编译类型时分配给它的,在编译时分配的类型的详细信息在代码级别是不知道的
没错。
这些细节是 CLR 自己知道的。
我不知道你在说什么“细节”或“CLR 知道”是什么意思。
我听说 CLR 中的这些匿名类型被视为仅作为引用类型。
你没听错。
所以我的问题是,是否在编译时创建一个新类型,如类或结构,对应于匿名类型中定义的只读属性?
是的。创建了一个新类。
注意在一个程序集中如果有两个匿名类型具有相同的属性名称,相同的属性类型,以相同的顺序,那么只创建一个类型。这是由语言规范保证的。
运动:
// Code in Assembly B:
public class B { protected class P {} }
// Code in Assembly D (references assembly B)
class D1 : B {
public static object M() { return new { X = new B.P() }; }
}
class D2 : B {
public static object M() { return new { X = new B.P() }; }
}
您需要在程序集 D 中生成一个 single 类声明,该声明具有 B.P 类型的属性 X,使得 D1.M().GetType() 等于 D2.M().GetType()。描述如何做到这一点。
【讨论】:
protected 关键字。我想它会是这样的:public abstract class CompilerMagic : B { public class AnonymousType { B.P x; public B.P X { get { return x; } } } }?
B.P 比AT.X 更难访问。而且,您不能将它嵌套在D1 或D2 下,因为受保护的属性X 在其他类中不可用......请启发我,@EricLippert。
匿名类型是直接从对象派生的类类型,不能转换为除对象之外的任何类型。编译器为每个匿名类型提供一个名称,尽管您的应用程序无法访问它。从公共语言运行时的角度来看,匿名类型与任何其他引用类型没有区别。
【讨论】:
除了没有程序员可访问的名称外,匿名类型非常简单:编译器根据您所做的分配生成它们,并负责将相同方法中的相同匿名类型正确地合并为单个运行时类型。
根据 C# 语言规范第 7.6.10.6 节,匿名类型始终是类,而不是 structs。表单的匿名对象初始化器
new { p1 = e1 , p2 = e2 , ... pn = en }
声明表单的匿名类型
class __Anonymous1 {
private readonly T1 f1 ;
private readonly T2 f2 ;
...
private readonly Tn fn ;
public __Anonymous1(T1 a1, T2 a2,…, Tn an) {
f1 = a1 ;
f2 = a2 ;
...
fn = an ;
}
public T1 p1 { get { return f1 ; } }
public T2 p2 { get { return f2 ; } }
...
public Tn pn { get { return fn ; } }
public override bool Equals(object __o) { … }
public override int GetHashCode() { … }
}
【讨论】:
这些匿名类直接派生自对象。通常,它们用于选择 LINQ 查询以将只读属性封装到单个对象中。
LINQ 示例(FirstName 和 LastName 作为 FullName):
public class Person {
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
}
IEnumerable<int> personIds = persons
.Select(p => new { Id = p.Id, FullName = p.FirstName + " " + p.LastName})
.Where(a => a.FullName == "John Smith")
.Select(a => a.Id);
【讨论】:
匿名类型是 C♯ 功能,在 CLI 上没有等效功能。它们被简单地编译为普通类型,编译器选择了一个非常长且非常复杂的名称。请注意,规范保证具有相同结构的两个匿名类型(在同一个程序集中)实际上是相同的类型,因此编译器也需要考虑到这一点,并且只生成一种类型(并为两者生成相同的名称使用网站)。
【讨论】: