// Создать свойство для каждой закрытой переменной-члена...
// Переопределить метод ToStringO для учета основных
// переменных-членов...
// Переопределить методы GetHashCode и Equals для работы
// с эквивалентностью на основе значений...
}
Как
видите, задача не обязательно оказывается настолько простой. Вам потребуется не только написать большой объем кода, но еще и сопровождать дополнительный класс в системе. Для временных данных подобного рода было бы удобно формировать специальный тип на лету. Например, пусть необходимо построить специальный метод, который принимает какой-то набор входных параметров.Такие параметры нужно использовать для создания нового типа данных, который будет применяться внутри области действия метода. Вдобавок желательно иметь возможность быстрого вывода данных с помощью метода
ToString
и работы с другими членами
System.Object
. Всего сказанного можно достичь с помощью синтаксиса анонимных типов.
Определение анонимного типа
Анонимный тип определяется с использованием ключевого слова
va
r (см. главу 3) в сочетании с синтаксисом инициализации объектов (см. главу 5). Ключевое слово
var
должно применяться из-за того, что компилятор будет автоматически генерировать новое определение класса на этапе компиляции (причем имя этого класса никогда не встретится в коде С#). Синтаксис инициализации применяется для сообщения компилятору о необходимости создания в новом типе закрытых поддерживающих полей и (допускающих только чтение) свойств.
В целях иллюстрации создайте новый проект консольного приложения по имени
AnonymousTypes
. Затем добавьте в класс
Program
показанный ниже метод, который формирует новый тип на лету, используя данные входных параметров:
static void BuildAnonymousType( string make, string color, int currSp )
{
// Построить анонимный тип с применением входных аргументов.
var car = new { Make = make, Color = color, Speed = currSp };
// Обратите внимание, что теперь этот тип можно
// использовать для получения данных свойств!
Console.WriteLine("You have a {0} {1} going {2} MPH",
car.Color, car.Make, car.Speed);
// Анонимные типы имеют специальные реализации каждого
Обратите внимание, что помимо помещения кода внутрь функции анонимный тип можно также создавать непосредственно в строке:
Console.WriteLine("***** Fun with Anonymous Types *****\n");
// Создать анонимный тип, представляющий автомобиль.
var myCar = new { Color = "Bright Pink", Make = "Saab",
CurrentSpeed = 55 };
// Вывести на консоль цвет
и производителя.
Console.WriteLine("My car is a {0} {1}.", myCar.Color, myCar.Make);
// Вызвать вспомогательный метод для построения
// анонимного типа с указанием аргументов.
BuildAnonymousType("BMW", "Black", 90);
Console.ReadLine;
В настоящий момент достаточно понимать, что анонимные типы позволяют быстро моделировать "форму" данных с небольшими накладными расходами. Они являются лишь способом построения на лету нового типа данных, который поддерживает базовую инкапсуляцию через свойства и действует в соответствии с семантикой на основе значений. Чтобы уловить суть последнего утверждения, давайте посмотрим, каким образом компилятор C# строит анонимные типы на этапе компиляции, и в особенности — как он переопределяет члены
System.Object
.
Внутреннее представление анонимных типов
Все анонимные типы автоматически наследуются от
System.Object
и потому поддерживают все члены, предоставленные этим базовым классом. В результате можно вызывать метод
ToString
,
GetHashCode
,
Equals
или
GetType
на неявно типизированном объекте
myCar
. Предположим, что в классе
Program
определен следующий статический вспомогательный метод: