|Deep C#: Strong Typing|
|Written by Mike James|
|Monday, 13 September 2021|
Page 1 of 2
C# is a strongly typed langauge but what does this mean and why is it good. Find out in this extract from my new book Deep C#.
Buy Now From Amazon
C# is a language that started out in the style of Java with strong typing and very little extra. Over time it has added features in a careful and thoughtful way to include things that you might not associate with a strongly typed, "traditional" object-oriented language. For many this is what makes C# special. Today C# has late binding with dynamic typing. This provides the modern C# programmer with idioms that previously would have been thought out of the question. Dynamic typing goes directly against the static strong typing that is supposed to catch many errors at compile time that otherwise would only appear at runtime.
The worry is that by introducing such loose practice C# might become a mess by adopting models that go against its birthright of strict typing and object-oriented procedural code. What is interesting is the way that C# combines the best of both worlds - but see if you agree.
Strong Typing and Type Safety
The first problem we have to overcome is that not everyone is very clear about what “strong typing” means. So the first thing to investigate is the nature of strong typing.
Strong typing is all about trying to detect a particular error at compile time. The error in question is calling a method, or using a property, of an object that it doesn’t have. That is, strong typing will flag an error at compile time if myObject.myMethod(); isn’t possible because myObject doesn’t have a myMethod. A type is defined by a bundle of properties and methods and when you claim that a variable is referencing a particular type then the system assumes that the type has exactly those properties and methods. So, when you write:
MyClass myObject=new MyClass();
myObject is assumed to have all of the properties and methods of MyClass.
If, after this, you write:
then the compiler can check that MyClass defines a function called myMethod at compile time and refuse to let the program run if it doesn’t.
Strong typing is a bookkeeping method of making sure that you only use what you declare you have implemented.
What this means is that every variable has an assigned type, provided by its class definition, and it can only reference an object that has been created from that type definition. Of course, and you are probably so familiar with this idea that you don’t notice it, we generally allow derived classes to stand in for a base class, or more generally any class that is lower in the inheritance chain.
So we are accustomed to the idea that there is nothing wrong with:
MyClassA myObjectA; . . . myObjectA = myObjectB;
as long as myObjectB has inherited MyClassA somewhere in its inheritance chain.
Why is this permitted? The reason is that we can assume that any method or property that MyClassA has is present in myObjectB. This is sometimes expressed as the Liskov Substitution Principle which is informally anywhere you can use a base class you can use a derived class. Of course, this is nonsense as the derived class can override methods in the base class and if these are used in place of the base class methods then things can go wrong.
The Liskov principle is only true if we work to make it true. In particular, it is only true by default if the language specifies early binding, more of this later. This leads to the idea of being “type safe”. An instruction is type safe if it is possible to use type to check at compile time that it will not cause a run-time error. If you want to determine that some feature of any language is type safe simply ask yourself if it is possible to determine at compile time that a run-time error is extremely unlikely on the basis of the types in use.
The strong typing bookkeeping allows derived types to be used in place of the base type simply because they have all of the properties of the base type.
Why is compile-time checking so important?
If you make a type error that isn’t detected at compile time, it will most probably be detected at run time with a run-time exception. The reason that it is only probably detected is a matter of coverage. A syntactically correct program can cause a run-time error, but only if the inputs to the program cause it to exercise that part of the program where the error is located. You may run the program and never trigger the error, only for your first user to encounter it because they use the program differently to you. If you could guarantee to find all run-time errors on the first run there would be little difference between compile-time and run-time detection. This is the problem of “coverage”.
It is often claimed that strong typing helps with entering the correct code in the first place via some sort of intelligent prompting. This is sometimes true, but it is often used to make up for poor documentation and the same facility can be implemented without having to enforce strong typing, or typing of any sort. The point is that the sort of errors that strong typing and type safety detect are very trivial – they have to be to be detected at compile time. It really does come down to determining that the object being used has the properties and methods you actually use.
Type safety is a very limited form of safety.
|Last Updated ( Tuesday, 14 September 2021 )|