C# is well known for its nominative type system. This means that C# identifies types and their relationships based on their names. That is why C# does not allow you to create two types with the same name, even if they have a different public interface – the C# compiler only cares about the name of the type.
There is another type system called a structural type system. A structural type system recognizes the relationships between types based on the structure (i.e. the public members) of those types – names do not come into consideration. For example, if C# had a structural type system, these two classes would be considered compatible:
class Foo
{
public String Name { get; set; }
}
class Bar
{
public String Name { get; set; }
}
Because both of these types are declared with a String property called Name, a structural type system would happily work with both of these types as if only one type were declared. This means that instances of Foo could be assigned to a reference typed as Bar and instances of Bar could be passed to methods expecting a argument of type Foo. (This is similar to duck typing except that duck typing binds member calls at execution time whereas a structural type system does static type checking at compile time.)
Many developers do not realize that C# already supports structural typing in certain scenarios (most of them are well-concealed to reduce confusion). Here is the most common place where C# employs structural typing concepts:
foreach (Object o in someCollection) { }
Did you know that when the C# compiler encounters a foreach in your code it uses a structural approach to determine whether or not what you are enumerating a valid collection?
Consider this example:
class Foo { }
class Example
{
static void Main()
{
Foo foo = new Foo();
foreach (var f in foo) { }
}
}
This code does not compile, but fortunately the compiler’s error message gives us some indication of how to fix the problem:
foreach statement cannot operate on variables of type ‘Foo’ because ‘Foo’ does not contain a public definition for ‘GetEnumerator’
Notice that the compiler doesn’t say that we ought to implement IEnumerable – rather it tells us that our Foo class doesn’t provide a GetEnumerator method. This is because the compiler is only concerned about the structure of Foo, not it’s nominative relationships to an interface that would ensure that the GetEnumerator method was present.
Let’s fix the error by adding a GetEnumerator method to Foo like the compiler requested:
using System.Collections;
class Foo { public IEnumerator GetEnumerator() { return null; } } class Example { static void Main() { Foo foo = new Foo(); foreach (var f in foo) { } } }
Interestingly enough, this does compile – structural typing at work!
Here is another example of C#’s existing support for structural typing:
var list = new List<String> {
"hello", "world"
};
When the C# compiler encounters a collection initializer like this one, it uses a structural approach to determine if the type that is being initialized is actually a collection. Rather than using a nominative approach and checking to see if the type implements ICollection<T>, the compiler actually only checks to see if the type implements IEnumerable<T> (a nominative type check) and if the type exposes a public Add method. It is this last check for Add that is structural in nature since the compiler is checking for the presence of a method, not a nominative relationship on the type itself.
The purpose of this article was to show two areas where C# supports structural typing. I would be interested in discussing any other hidden areas of C# that are structural in nature – are you familiar with any that I didn’t cover?
Stay tuned for a follow-up article discussing two structural features I would like to see implemented in C# 5.
Edit: Here is the follow up article: Two structural additions to C#.
6 Comments
The big one you didn’t cover is LINQ query syntax. There’s a post on my blog (linked above) where I explain why it works this way …
Very helpful spot. Thank you very much
There have been so many times where I wish the compiler had a bit more support for duck typing. Take this for example:
public static T Add(T a, T b) { return a + b; }
There is no way for this to compile. Why couldn’t the compiler check if the type has a + operator implemented?
Google’s Go supports structural typing, however I’m strongly opposed to this kind of typing, mostly for the same reasons I’m opposed to weak/duck typing. Let’s take an example that illustrates where things could go wrong with structural typing. Let’s say we have two classes that represent 2D coordinates. Both have only float properties X and Y. First class represents cartesian system and the other a polar one. And because public interface is same for both classes a programmer might be tempted to use one class in a part of code that expects the other one. Compiler won’t be able to inform you that you did something wrong. See, the problem is about contracts. If a class implements an interface then it means it also conforms to all the minute behavioral details that are required of a class that implemets it. If a class belongs to an interface merely by having the same public members fingerprint then you can’t be confident a class will conform to what you believe a class implementing the interface is supposed to. Now if you want to include into structural typing also internal implementation of each member then you get back most of nominative type system with added support for type aliases and we are almost back from where we started. :)
Here’s another example:
var a = new { Id = 123, Value = “abc” }; var b = new { Id = 456, Value = “def” };
// will print “True” System.Console.WriteLine((typeof(a) == typeof(b)).ToString());
That is, anonymous types with exactly the same properties are considered as being of the same type by the compiler. This feature can be abused to achieve returning anonymous type instances, as demonstrated by Jon Skeet: http://msmvps.com/blogs/jon_skeet/archive/2009/01/09/horrible-grotty-hack-returning-an-anonymous-type-instance.aspx
I think this demonstrates one of the biggest problems in C#. That is, you can’t write your own foreach that has this same behavior. The fact that only the compiler gets to play this structural typing game bothers me to no end.
My guess is that this hack was done to support enumerating over arrays. Still, if language constructs are going to be built in this way, we should have the ability to make them too.
One Trackback/Pingback
[...] the original post: Togaroga / What's in a name? (C#'s hidden support for structural … If you enjoyed this article please consider sharing [...]
Post a Comment