Deep C#: Strong Typing
Written by Mike James   
Monday, 13 September 2021
Article Index
Deep C#: Strong Typing
Binding

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#.

Deep C#

 Buy Now From Amazon

DeepCsharp360

 Chapter List

  1. Why C#?
    I Strong Typing & Type Safety
  2. Strong Typing
       Extract 
    Why Strong Typing
  3. Value & Reference
  4.    Extract Value And Reference
  5. Structs & Classes
       Extract
    Structs & Classes 
  6. Inheritance
      
    Extract
    Inheritance
  7. Interfaces & Multiple Inheritance
      
    Extract Interface
  8. Controlling Inheritance
    II Casting & Generics
  9. Casting - The Escape From Strong Typing
      
    Extract Casting I
  10. Generics
  11. Advanced Generics
  12. Anonymous & Dynamic
    Typing
    III Functions
  13. Delegates
  14. Multicast Delegates
  15. Anonymous Methods, Lambdas & Closures
    IV Async
  16. Threading, Tasks & Locking
  17. The Invoke Pattern
  18. Async Await
  19. The Parallel For ***NEW!
    V Data - LINQ, XML & Regular Expressions
  20. The LINQ Principle
  21. XML
  22. LINQ To XML
  23. Regular Expressions
    VI Unsafe & Interop
  24. Interop
  25. COM
  26. Custom Attributes
  27. Bit Manipulation
  28. Advanced Structs
  29. Pointers 

Extra Material

 <ASIN:1871962714>

 <ASIN:B09FTLPTP9>

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

Strong typing, which is a guiding principle for C#, is also the defacto default for most languages. The exceptions, Python and JavaScript for example, suffer much criticism for not being strongly typed and they also are subject to maintainers who slowly try to convert them into strongly typed languages in one way or another. As already mentioned, C# has been moving in the opposite direction. Starting out with nothing but strong types it has been slowly adding features that attempt to make it as easy to use as some of the dynamically typed languages. However, every modification to C# has tried to preserve its strong typing and type-safe approach. It is still a guiding principle and it is important to understand what it is all about and what its advantages and disadvantages are.

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:

myObject.myMethod()

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 ( Monday, 04 October 2021 )