Выражения создания анонимных объектов

Выражение создания анонимного объекта используется для создания объекта анонимного типа.

anonymous-object-creation-expression:
new anonymous-object-initializer

anonymous-object-initializer:
{ member-declarator-listopt }
{ member-declarator-list , }

member-declarator-list:
member-declarator
member-declarator-list , member-declarator

member-declarator:
simple-name
member-access
base-access
identifier = expression

Инициализатор анонимного объекта объявляет анонимный тип и возвращает экземпляр этого типа. Анонимный тип — это безымянный тип класса, который наследуется непосредственно от класса object. Члены анонимного типа представляют собой последовательность свойств, доступных только на чтение, выводимых из инициализатора анонимного объекта, использованного для создания экземпляра типа. В частности, инициализатор анонимного объекта вида

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() { … }
}

где каждый Tx является типом соответствующего выражения ex. Выражение, используемое в деклараторе члена, должно иметь тип. Поэтому, если выражение в деклараторе члена равно NULL или является анонимной функцией, возникает ошибка времени компиляции. Ошибка времени компиляции также возникает, если выражение имеет небезопасный тип.

Имена анонимного типа и параметра его метода Equals автоматически создаются компилятором, и на них нельзя ссылаться в тексте программы.

В рамках одной программы два инициализатора, которые задают последовательность свойств с одинаковыми именами и типами времени компиляции в одном порядке, создадут экземпляры одного анонимного типа.

В этом примере

var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;

присваивание на последней строке допускается, потому что p1 и p2 имеют один анонимный тип.

Методы Equals и GetHashcode для анонимных типов перегружают методы, унаследованные от класса object, и определяются через методы Equals и GetHashcode для свойств таким образом, что два экземпляра одного анонимного типа будут равны, только если равны все их свойства.

Название декларатора члена можно сократить до простого имени (§7.5.2), доступа к члену (§7.5.4) или доступа base (§7.6.8). Такой способ называется инициализацией проекции и представляет собой удобный способ для объявления присваивания свойству с таким же именем. В частности, деклараторы членов вида

identifier expr . identifier

в точности совпадают со следующими выражениями соответственно:

identifer = identifier identifier = expr . identifier

Таким образом, при инициализации проекции идентификатор позволяет выбирать и значение, и поле или свойство, которому присвоено это значение. С интуитивной точки зрения при инициализации проекции проецируется не только значение, но и имя значения.

Оператор typeof

Оператор typeofиспользуется для получения объекта System.Typeдля типа.

typeof-expression:
typeof ( type )
typeof ( unbound-type-name )
typeof ( void )

unbound-type-name:
identifier generic-dimension-specifieropt
identifier :: identifier generic-dimension-specifieropt
unbound-type-name . identifier generic-dimension-specifieropt

generic-dimension-specifier:
< commasopt >

commas:
,
commas ,

Выражение typeof первого типа состоит из ключевого слова typeof, за которым следует тип в скобках. Результатом выражения этого типа является объект System.Type для указанного типа. Для любого типа существует только один объект System.Type. Это означает, что для типа T всегда выполняет равенство typeof(T) == typeof(T). Тип не может быть dynamic.

Выражение typeof второго типа состоит из ключевого слова typeof, за которым следует непривязанное имя типа в скобках. Непривязанное имя типа очень похоже на имя типа (§3.8) за исключением того, что непривязанное имя типа содержит универсальные спецификаторы измерений, тогда как имя типа содержит списки аргументов типа. Если операнд выражения typeof является последовательностью токенов, соответствующей правилам формирования непривязанного имени типа и имени типа, а именно если он не содержит ни универсальный спецификатор измерения, ни список аргументов типа, то последовательность токенов считается именем типа. Значение непривязанного имени типа определяется следующим образом.

· Последовательность лексем преобразуется в имя типа путем замены каждого универсального спецификатора измерения на список аргументов типа с таким же числом запятых и ключевым словом object, как и у каждого аргумента типа.

· Получившееся имя типа вычисляется, причем все ограничения параметра типа игнорируются.

· Непривязанное имя типа разрешается в непривязанный универсальный метод, связанный с результирующим сформированным типом (§4.4.3).

Результатом выражения typeof является объект System.Type для результирующего непривязанного универсального типа.

Выражение typeof третьего типа состоит из ключевого слова typeof, за которым следует ключевое слово void в скобках. Результатом выражения этого типа является объект System.Type, обозначающий отсутствие типа. Объект типа, возвращаемый выражением typeof(void), отличается от объекта типа, возвращаемого для любого типа. Этот специальный объект типа удобно использовать в библиотеках классов, которые допускают отражение в методы языка, где для этих методов желательно иметь способ представления типа возвращаемого значения, включая методы, возвращающие void, с помощью экземпляра System.Type.

Оператор typeof можно использовать в параметре типа. Результатом является объект System.Type для типа времени выполнения, который был связан с параметром типа. Оператор typeof также можно использовать в сформированном типе или непривязанном универсальном типе (§4.4.3). Объект System.Type для непривязанного универсального типа отличается от объекта System.Type для типа экземпляра. Тип экземпляра всегда является закрытым сформированным типом во время выполнения, поэтому его объект System.Type зависит от используемых во время выполнения аргументов типа, тогда как у непривязанного универсального типа аргументов типа нет.

Пример:

using System;

class X<T>
{
public static void PrintTypes() {
Type[] t = {
typeof(int),
typeof(System.Int32),
typeof(string),
typeof(double[]),
typeof(void),
typeof(T),
typeof(X<T>),
typeof(X<X<T>>),
typeof(X<>)
};
for (int i = 0; i < t.Length; i++) {
Console.WriteLine(t[i]);
}
}
}

class Test
{
static void Main() {
X<int>.PrintTypes();
}
}

получается следующий вывод:

System.Int32
System.Int32
System.String
System.Double[]
System.Void
System.Int32
X`1[System.Int32]
X`1[X`1[System.Int32]]
X`1[T]

Обратите внимание, что int и System.Int32 являются одним типом.

Также обратите внимание, что результат выражения typeof(X<>) не зависит от аргумента типа, а результат typeof(X<T>) зависит.

Наши рекомендации