Ахтунг, товарищ. Перед тем как сомневаться в профессионализме своих коллег очень советую хотя бы немного сомневаться в собственном. Это как минимум изредка полезно. А теперь ноги в руки и вперёд читать про
таблицы, виртуальность и методы. Это не просто полезно, но архиважно. Учитывая, собственно, вопрос.
А вопрос состоял в следующем. Вот у нас есть объекты, друг от друга наследуются, имплементируются и всё круто-прекрасно, но блджад. Зачем они нужны-то? Нет, не агрегация данных, хотя и она тоже. Нет, не агрегация реализации, хотя и она тоже. И нет, нет, не просто потому что. Хотя и это тоже... А вот подумай о такой ситуации. Есть библиотека. Да не важно какая, ну пусть это будет UI/UX. Вот есть абстрактный (!) класс кнопка. Почему абстрактный? Да потому что любая кнопка - это кнопка. Это как бы класс над классом. Кнопка может быть красной, может быть кликабельной, может становится не кликабельной после нажатия, может открывать диалог выбора файлов, а может закрывать приложение к чертям. В общем, много чего может уметь делать кнопка. И вот эту кнопку хочется поставить на формочку. И сделал создатель библиотеки такой прекрасный метод у формы PutAnyButtonHere(Button btn, Point xy). Но... Чёрт, чувак, мы должны передать объект класса Button, а у нас какой-нибудь наследник MyBestButton : BestAbstractLibraryButton, который наследник той самой Button. И у Button декларирован такой прекрасный метод TimeWhenUserClickOnMe(Point xy, AnotherInfo somethinElse), собственно... Получается, что где-то в недрах той самой библиотеки, когда автор прекрасной UI/UX библиотеки вызывает этот самый метод он должен вызвать НАШУ реализацию, а не реализацию BestAbstractLibraryButton. И уж точно не несуществующую реализацию Button (ибо метод там чисто-виртуальный). Идея виртуальных методов изначально именно в том, что есть особая таблица, которая хранит реализации отдельно, объекты отдельно, но когда мы пытаемся вызывать у объекта виртуальный метод происходит такая магия, которую принято называть поздним связыванием.
А ключевые слова это просто для того, чтобы программист читая чужие полотна кода хоть ну немного имел возможность хоть как-то их понять. Что конкретно хотел программист в конкретной ситуации. И, если в Java все методы по дефолту виртуальные и ничерта не понятно из чужих полотен, хотел здесь программист позднее связывание, или это просто агрегация функционала, то в шарпе данной проблемы "как бы нет", язык порой чрезвычайно многословен на, казалось, такие банальности. Что в итоге изредка, но экономит чёртову тучу времени.
Сухой остаток. Если мы вызываем виртуальный метод у объекта класса наследника некоторого супер-класса имея ссылку типа супер-класса, то вызывается реализация метода наследника. Если мы вызываем не виртуальный метод у объекта класса наследника некоторого супер-класса имея ссылку типа супер-класса, то вызывается реализация метода супер-класса. В остальном поведение тривиальное.
Отсебятина. Не понимая данных вещей невозможно говорить, что принципы объектно-ориентированного программирования были усвоены. Советую прочитать "Философия Java" (вообще там Java скорее как язык для примеров, в общем-то ООП оно и в Африке ООП). Ну и пытаться разобраться в точке зрения любого человека, особенно если его компетенция хоть чем-то подтверждена (например, успешным трудоустройством).