Virtual and override are definitely necessary if you want your inheritance to work the way you expect it to -- they're not just decorative, they really do change the way the software behaves. It's actually something that I've run into in real-life programming. In fact, it can lead to some pretty nasty bugs if you're not careful.
walbaloushi's right with the Penguin example. Let's say you've got Penguin inheriting from Bird, where Penguin.Fly() says "I don't fly!" and Bird.Fly() says "Flap, flap". And let's say you leave out the override keyword. If you create a new instance of Penguin like this:
Penguin penguin = new Penguin();
penguin.Fly();
it'll say "I don't fly!" just like you expect. But if you declare it like this:
Bird bird = new Penguin();
bird.Fly();
then it'll say, "Flap, flap". But if you put that override keyword back, it'll say "I don't fly!" like you'd expect. But you can only put that override keyword for Penguin.Fly() if you've marked Bird.Fly() virtual -- if you don't, your program won't build.
This may seem like crazy behavior, but it actually makes sense. Sometimes you'll want exactly this behavior, where your object behaves differently based on how it's declared and what context it's used in.
I've been meaning to do
another C# blog post aimed at helping people learn C#. I'll give it some thought and see if I can come up with a good example that can help you explore this, since it's something that doesn't quite make sense until you see it.