
.Net-detective: about interfaces and reflection
Recently, I was interested in the structure of such an ancient .Net interface as IList. Why I was interested in this is a separate long story, of which I, probably. I'll tell you next time.
To study this interface in more detail, I first decided to see what methods it declares. Using reflection:
... I got the following results:
As you can easily guess, the prefixes indicate the getter and setter of the interface properties, that is, with us. besides all methods, there are three more properties: Item with the ability to write and read, and two reodonly properties IsReadOnly and IsFixedSize. In addition, obscure duplication of names is noticeable: get_IsReadOnly // IsReadOnly, get_IsFixedSize // IsFixedSize, get_Item // set_Item // Item. In order to deal with this, I had to change the code a bit and, in addition to the class properties themselves, also print the names of the types of properties. The following code has turned out:
This code has given me a more detailed idea. what the interface contains:
So, it became clear that get_IsReadOnly is a getter method of the IsReadOnly property (which is a separate member of the interface, along with the method), just like get_IsFixedSize is a method called when the IsFixedSize property is read. The Item property here has both a setter and a getter, that is, it can be accessed as if reading it. and changing.
Realizing this, I immediately wondered: what will happen if the methods for the properties are already predefined.
Having quickly cooked up the first example, I became convinced. that such code will not compile:
The compiler produces the following error:
The second property of Item turned out to be more interesting. My hunch that an indexer is hiding behind a property with the same name turned out to be true and I was able to cook up a second uncompiled class, this time with an indexer:
This time the error description was slightly different, but the essence of the problem was also clear:
However, reverse attempts to “trick” the compiler by explicitly implementing the methods required by the indexer were unsuccessful. The following two class examples compiled without problems,
However, an attempt to access class instances using an indexer led to a compilation error:
Thus. the compiler checks for the syntax of the indexer at compile time, which prevents us from using such a tricky way to determine our own indexer.
The following conclusions can be drawn from a small investigation:
If the article is pleasant. then in the next article I will tell you what I was interested in the IList interface.
To study this interface in more detail, I first decided to see what methods it declares. Using reflection:
foreach(var tp in typeof(IList).GetMembers())
Console.WriteLine(tp.Name);
... I got the following results:
get_Item
set_Item
Add
Contains
Clear
get_IsReadOnly
get_IsFixedSize
IndexOf
Insert
Remove
RemoveAt
Item
IsReadOnly
IsFixedSize
As you can easily guess, the prefixes indicate the getter and setter of the interface properties, that is, with us. besides all methods, there are three more properties: Item with the ability to write and read, and two reodonly properties IsReadOnly and IsFixedSize. In addition, obscure duplication of names is noticeable: get_IsReadOnly // IsReadOnly, get_IsFixedSize // IsFixedSize, get_Item // set_Item // Item. In order to deal with this, I had to change the code a bit and, in addition to the class properties themselves, also print the names of the types of properties. The following code has turned out:
foreach(var tp in typeof(IList).GetMembers())
Console.WriteLine("{0} -- {1}", tp.Name, Enum.GetName(tp.MemberType.GetType(), tp.MemberType));
This code has given me a more detailed idea. what the interface contains:
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
So, it became clear that get_IsReadOnly is a getter method of the IsReadOnly property (which is a separate member of the interface, along with the method), just like get_IsFixedSize is a method called when the IsFixedSize property is read. The Item property here has both a setter and a getter, that is, it can be accessed as if reading it. and changing.
Realizing this, I immediately wondered: what will happen if the methods for the properties are already predefined.
Having quickly cooked up the first example, I became convinced. that such code will not compile:
public class Test1
{
public int A { get; set; }
public int get_A()
{
return 0;
}
}
The compiler produces the following error:
Type 'TestArray.Test1' already reserves a member called 'get_A' with the same parameter types
The second property of Item turned out to be more interesting. My hunch that an indexer is hiding behind a property with the same name turned out to be true and I was able to cook up a second uncompiled class, this time with an indexer:
public class Test2
{
public int Item { get; set; }
public int this[int num]
{
get
{
return 0;
}
}
}
This time the error description was slightly different, but the essence of the problem was also clear:
The type 'TestArray.Test2' already contains a definition for 'Item'
However, reverse attempts to “trick” the compiler by explicitly implementing the methods required by the indexer were unsuccessful. The following two class examples compiled without problems,
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)
{
}
}
However, an attempt to access class instances using an indexer led to a compilation error:
Cannot apply indexing with [] to an expression of type 'TestArray.Test4'
Thus. the compiler checks for the syntax of the indexer at compile time, which prevents us from using such a tricky way to determine our own indexer.
The following conclusions can be drawn from a small investigation:
- The C # compiler uses a fairly simple and obvious algorithm to generate the names of internal methods when using syntactically “sugar” constructs such as indexer and property setter / getter.
- Even without inventing complex names for your methods, you can get a problem with name collisions, which will not be so easy to understand without knowing the details of the .Net internal device.
- The Item property and its corresponding setters and getters are converted by compilers to the syntax of the postfix operator “square brackets” (= indexer), which is the syntactic sugar for the get_Item () construct.
If the article is pleasant. then in the next article I will tell you what I was interested in the IList interface.