匿名类型 C#

11
我了解到匿名类型没有预定义的类型。类型是由编译器在编译时分配的,在代码级别无法知道分配的类型详细信息;这些详细信息只对CLR本身知道。我听说CLR中的这些匿名类型只被视为引用类型。那么我的问题是,是否在编译时会创建一个新的类型,类似于类或结构体,与在匿名类型中定义的只读属性相对应?
5个回答

12

我知道匿名类型没有预定义的类型。

正确。除了 object 类型之外,匿名类型没有其他基础类型。

类型是由编译器在编译时分配给它的,分配的类型细节无法在代码级别上知道。

没错。

这些细节只有 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, 该类有一个类型为B.P的属性X,以便D1.M().GetType()等于D2.M().GetType()。请描述如何实现。


在同一个程序中,指定相同名称和编译时类型的属性序列的两个匿名对象初始化器将按照相同的顺序生成相同匿名类型的实例。-(C# 4.0规范,7.6.10.6) - Xiaoy312
@Xiaoy312:没错。现在,如果你需要在D汇编中为上述匿名类型生成这样的类型,如果你必须编写源代码,那么这个类型会是什么样子? - Eric Lippert
实际上,这仍然不起作用,因为B.PAT.X更不可访问。而且,您不能将其嵌套在D1D2下面,因为受保护的属性X在其他类中不可用...请给我指点一下,@EricLippert。 - Xiaoy312
@Xiaoy312:你的反对意见都是正确的,但它仍然可以完成。提示:在C# 1.0中无法完成。 - Eric Lippert
@PabloH:我所知道的所有实现都会使这些类型不同。然而,新的元组类型可以在程序集之间统一。 - Eric Lippert
显示剩余3条评论

6
匿名类型是从object直接派生的类类型,除了object类型之外,无法将其转换为任何其他类型。编译器为每个匿名类型提供名称,但您的应用程序无法访问它。从公共语言运行时的角度来看,匿名类型与任何其他引用类型没有区别
来源:https://msdn.microsoft.com/en-us/library/bb397696.aspx

3
除了没有可供程序员访问的名称外,匿名类型非常直接:编译器根据您所做的赋值生成它们,并负责将来自同一方法的相同匿名类型正确合并为单个运行时类型。
根据C#语言规范,第7.6.10.6节,匿名类型始终是类,而不是struct。形式为的匿名对象初始化程序
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() { … }
}

尽管从逻辑上讲,那是生成的代码,但实际上这是相当简化的。请查看我的答案以了解原因。 - Eric Lippert

1

匿名类型是 C♯ 的一项特性,在 CLI 上没有等价物。它们只是编译成普通类型,编译器选择一个非常长且非常复杂的名称。请注意,规范保证具有相同结构的两个匿名类型(在同一程序集内)实际上是相同的类型,因此编译器还需要考虑这一点,并仅生成一个类型(并为两个使用处生成相同的名称)。


1

这些匿名类是直接从对象派生的。通常在选择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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接