C# is primarily a statically typed language, meaning that before your code even runs, the compiler determines a type for each variable, property, method argument, and expression. This is called the static type of the relevant item, because it never changes after being established at compile time. There is some scope for runtime variability, thanks to inheritance and interfaces. A variable whose static type is a class can refer to an object whose type derives from that class; if the static type is an interface, the variable can refer to any object that implements that interface. With virtual methods or interfaces, this leads to runtime selection of which methods get invoked, but the variation is strictly circumscribed by the rules of the type system. Even with virtual method dispatch, the compiler knows which type defined the method you are invoking even if some derived type may have overridden it.
Dynamic languages take a much more relaxed view.
The type of any variable or expression is determined by whatever value it
happens to have at runtime. This means that a particular piece of code’s
arguments and variables could have different types each time it runs, and
the compiler knows nothing about what those types may be. Of course, that’s
true for variables with a static type of
object in C#, but the problem with
object is that you can’t do much with it. And this is the critical difference between static and dynamic typing: with static typing, the compiler ...