Because a struct is a value; a struct that contains a float is exactly 4 bytes long. There is no object-header that indicates what the concrete type is: the thing that knows the type is the compiler (and the resulting IL) - the type information is not self-contained in the value. So, regular polymorphism cannot work. If you do:
MyType type = new MySubType(); // if this was a struct and if this compiled
then from the compiler's perspective, the type of that is MyType. There is no notion, after the assignment, that this is or ever was a MySubType. Indeed, you can use unsafe code to force the bits of one value-type into another.
Additionally, this same facet of structs means that you would not be able to add fields in sub-types: the size has to be fixed, which makes this impossible.
Basically, if you are thinking in terms of inheritance, you are using structs incorrectly; actually, most times that I see people using structs they are using them incorrectly. structs should represent values (ideally immutable etc).
Is there really much real world use for a struct
Yes; for representing values - a ComplexNumber type for example; and for some highly optimized scenarios where GC is prohibitive, but which only affects a small number of developers. Here's one example of this; another common example is "games", but: switching between class and struct is a much bigger change than changing the keyword. You need to know what you are doing, and why you are doing it.
What you should not do is things like:
// ***BAD CODE: DO NOT DO***
public struct Customer
{ // I don't need inheritance; a struct will be faster and smaller
// yay me, I'm awesome
public int Id;
public string Name;
}
The above is clearly best represented as a class. Also: properties.
I know they are smaller than an object
In some ways, this is not true; for example, most useful structs are larger than a reference - which can be problematic if you are passing super-sized structs around on the stack without thinking what you are doing.