I am reading the Clean Architecture by Robert C. Martin and having trouble understanding of polymorphism concept.
As known there was some approaches to achieve polymorphic behavior at the before-OOP era. For e.g. for C (this was discussed in the SO, but I am following to the (simplified) book examples):
// main.c
#include <stdio.h>
int main () {
    int c = getchar(); // read from the standard STDIN
}
// console.c
#include "file.h" //defines struct FILE type with 5 pointers to functions for every device driver: open, close, read, write, seek
// ... implementation of 5 functions specific for console driver here
struct FILE console = { open, close, read, write, seek }; // FILE variable with pointers to functions
If STDIN is defined as FILE* and points to console then  getchar() can be implemented as
extern struct FILE* STDIN;
int getchar() {
    return STDIN->read();
}
I understand this concept of polymorphic behavior of getchar (the STDIN can point to different variables of FILE at the different time - getchar() knows nothing about exact driver device), but is there any difference with OOP languages approach?
- The author mentions that C-approach forces to to follow the convention to initialize a pointer like STDINand it's usually dangerous.
But should we not do the same in the OOP languages like C#?
public interface FILE { /* */ }
public static class STDIN 
{
    public static FILE STDINinstance { get; set; } 
}
If I not take care about null checking of STDINinstance consumer of it (getchar()) gets NullReferenceException
- What about DI principle? Why can we not say the the C module with getchar()has dependency onFILEabstraction (like OOP-interface)?getchar()knows nothing aboutconsoleor other implementation ofFILE. Does it mean DI is possible in C?
- The main question for me: what's new in modern OOP languages related with polymorphism that was not aviable before?
 
    