CurrentSpeed = 55};

// Выполнить рефлексию того, что сгенерировал компилятор.

ReflectOverAnonymousType(myCar);

...

Console.ReadLine();

Вывод будет выглядеть примерно так:

***** Fun with Anonymous Types *****

obj is an instance of: <>f__AnonymousType0`3

Base class of <>f__AnonymousType0`3 is System.Object

obj.ToString() = { Color = Bright Pink, Make = Saab, CurrentSpeed = 55 }

obj.GetHashCode() = -564053045

Первым делом обратите внимание в примере на то, что объект myCar имеет тип <>f__AnonymousType0`3 (в вашем выводе имя типа может быть другим). Помните, что имя, назначаемое типу, полностью определяется компилятором и не доступно в коде C# напрямую.

Пожалуй, наиболее важно здесь то, что каждая пара "имя-значение", определенная с использованием синтаксиса инициализации объектов, отображается на идентично именованное свойство, доступное только для чтения, и соответствующее закрытое поддерживающее поле, которое допускает только инициализацию. Приведенный ниже код C# приблизительно отражает сгенерированный компилятором класс, применяемый для представления объекта myCar (его можно просмотреть посредством утилиты ildasm.exe):

private sealed class <>f__AnonymousType0'3'<'j__TPar',

  'j__TPar', j__TPar>'

  extends [System.Runtime][System.Object]

{

  // Поля только для инициализации.

  private initonly j__TPar i__Field;

  private initonly j__TPar i__Field;

  private initonly j__TPar i__Field;

  // Стандартный конструктор.

  public <>f__AnonymousType0(j__TPar Color,

    j__TPar Make, j__TPar CurrentSpeed);

  // Переопределенные методы.

  public override bool Equals(object value);

  public override int GetHashCode();

  public override string ToString();

  // Свойства только для чтения.

  j__TPar Color { get; }

  j__TPar CurrentSpeed { get; }

  j__TPar Make { get; }

}

<p id="AutBody_Root432">Реализация методов ToString() и GetHashCode()</p>

Все анонимные типы автоматически являются производными от System.Object и предоставляют переопределенные версии методов Equals(), GetHashCode() и ToString(). Реализация ToString() просто строит строку из пар "имя-значение". Вот пример:

public override string ToString()

{

  StringBuilder builder = new StringBuilder();

  builder.Append("{ Color = ");

  builder.Append(this.i__Field);

  builder.Append(", Make = ");

  builder.Append(this.i__Field);

  builder.Append(", CurrentSpeed = ");

  builder.Append(this.i__Field);

  builder.Append(" }");

  return builder.ToString();

}

Реализация GetHashCode() вычисляет хеш-значение, используя каждую переменную-член анонимного типа в качестве входных данных для типа System.Collections.Generic.EqualityComparer. С такой реализацией GetHashCode() два анонимных типа будут выдавать одинаковые хеш-значения тогда (и только тогда), когда они обладают одним и тем же набором свойств, которым присвоены те же самые значения. Благодаря подобной реализации анонимные типы хорошо подходят для помещения внутрь контейнера Hashtable

<p id="AutBody_Root433">Семантика эквивалентности анонимных типов</p>

Наряду с тем, что реализация переопределенных методов ToString() и GetHashCode() прямолинейна, вас может интересовать, как был реализован метод Equals(). Например, если определены две переменные "анонимных автомобилей" с одинаковыми наборами пар "имя-значение", то должны ли эти переменные считаться эквивалентными? Чтобы увидеть результат такого сравнения, дополните класс Program следующим новым методом:

static void EqualityTest()

{

  // Создать два анонимных класса с идентичными наборами

  // пар "имя-значение".

Перейти на страницу:

Похожие книги