.Net-детектив: об интерфейсах и рефлексии

от автора

Недавно меня заинтересовала структура такого древнего .Net-интерфейса как IList. Почему меня это заинтересовало — это отдельная длинная история, о которой я, наверное. расскажу в следующий раз.

Чтобы изучить этот интерфейс подробнее я сначала решил посмотреть какие же методы он декларирует. Воспользовавшись рефлексией:

 foreach(var tp in typeof(IList).GetMembers())        Console.WriteLine(tp.Name); 

… я получил следующие результаты:

get_Item set_Item Add Contains Clear get_IsReadOnly get_IsFixedSize IndexOf Insert Remove RemoveAt Item IsReadOnly IsFixedSize 

Как можно легко догадаться, префиксами обозначены getter и setter свойств интерфейса, то есть у нас. помимо всех методов, есть еще три свойства: Item c возможностью записи и чтения и два reodonly свойства IsReadOnly и IsFixedSize. Кроме того, заметно непонятное дублирование названий: get_IsReadOnly//IsReadOnly, get_IsFixedSize//IsFixedSize, get_Item//set_Item//Item. Для того, чтобы разобраться с этим, пришлось несколько поменять код и кроме самих свойств класса также выдать на печать названия видов свойств. Получился следующий код:

 foreach(var tp in typeof(IList).GetMembers())       Console.WriteLine("{0} -- {1}", tp.Name, Enum.GetName(tp.MemberType.GetType(), tp.MemberType)); 

Этот код дал мне уже более развернутое представление о том. что содержит интерфейс:

get_Item -- Method set_Item -- Method Add -- Method Contains -- Method Clear -- Method get_IsReadOnly -- Method get_IsFixedSize -- Method IndexOf -- Method Insert -- Method Remove -- Method RemoveAt -- Method Item -- Property IsReadOnly -- Property IsFixedSize -- Property 

Итак, стало понятно, что get_IsReadOnly — это метод-геттер свойства IsReadOnly (которое является отдельным членом интерфейса, наравне с методом), как и get_IsFixedSize это метод вызываемый при чтении свойства IsFixedSize. Свойство Item имеет здесь и сеттер и геттер, то есть к нему можно обращаться как читая его. так и изменяя.
Поняв это я сразу задался вопросом: а что будет, если методы для свойств уже будут предопределены.
Быстро состряпав первый пример я убедился. что вот такой код не будет компилироваться:

    public class Test1     {         public int A { get; set; }         public int get_A()         {             return 0;         }     } 

Компилятор выдает следующую ошибку:

Type 'TestArray.Test1' already reserves a member called 'get_A' with the same parameter types	 

Более интересным оказалось второе свойство Item. Моя догадка о том, что за свойством с таким названием скрывается индексер оказалась верна и я смог состряпать второй некомпилируемый класс, в этот раз с индексером:

    public class Test2     {         public int Item { get; set; }         public int this[int num]         {             get             {                 return 0;             }         }     } 

В этот раз описание ошибки было несколько другое, но суть проблемы была также понятна:

The type 'TestArray.Test2' already contains a definition for 'Item'	 

Однако обратные попытки «обмануть» компилятор путем явной имплементации методов, необходимых для индексера, не имели успеха. Следующие два примера классов компилировались без проблем,

    public class Test4     {         public int Item { get; set; }     }       public class Test3     {         public Test3 get_Item(int index)         {             return this;         }          public void set_Item(int index, Test3 value)         {          }     } 

Однако попытка обращения к инстанциям класса при помощи индексера приводила к ошибке компиляции:

Cannot apply indexing with [] to an expression of type 'TestArray.Test4'	 

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

Из проведенного небольшого расследования можно сделать следующие выводы:

  • С# компилятор использует довольно простой и очевидный алгоритм для генерации имен внутренних методов при использовании таких синтаксически «сахарных» конструкций как indexer и property setter/getter.
  • Даже не изобретая сложных имен для своих методов можно получить проблему с коллизией имен, которую не так легко будет осознать не зная подробностей внутреннего устройства .Net.
  • Свойство Item и соответствующие ему сеттеры и геттеры преобразуются компиляторов в синтаксис постфиксного оператора «квадратные скобки» (=индексер), являющегося синтаксическим сахаром для конструкции get_Item().

Если статья понравится. то в следующей статье я расскажу чем меня заинтересовал интерфейс IList.

ссылка на оригинал статьи http://habrahabr.ru/post/156035/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *