You almost had it (in theory):
public class Point { public virtual double sideA, sideB, radius, height; }
public class RightTriangle:Point { public override double sideA, sideB; }
To make properties and methods overridable in derived classes they have to be declared virtual in the base class. The overriding class has to declare the overriden property/method as override.
In practice you cannot make fields virtual, only properties and methods can be. So you have to change your code as follows:
public class Point {
public virtual double sideA { get; set; }
public virtual double sideB { get; set; }
public virtual double radius { get; set; }
public virtual double height { get; set; }
}
public class RightTriangle:Point {
public override double sideA { get; set; }
public override double sideB { get; set; }
}
However, you should not do this as it is very bad design. More below.
So what's the difference to new?
When a method is overriden it will be decided at runtime whether the overridden or the original implementation will be called.
So when a method receives a Point as argument, that Point instance might actually be a RightTriangle or a Circle.
Point p1 = new RightTriangle();
Point p2 = new Circle();
In the above example, both p1 and p2 are Points from the perspective of the code that uses p1 and p2. However, "underneath" they are actually instances of the derived classes.
So with my solution, when you acess p1.sideA for example, the the runtime will look "underneath" and check what the Point really is: Is it actually a RightTriangle? Then check if there is an overridden implmentation of sideA and call that one. Is it actually a Circle? Then do the same check (which will fail) and call the original implmentation of sideA.
The new qualifier however does something else. It will not override a method, but create a completely new one (with the same name), that is handled differently at compile time.
So with your solution, the compiler sees that you created a RightTriangle and stored it in a variable of type Point. When you now access p1.sideA for example, the compiler will compile this access in such a way that it reads sideA from the base class, since the instance variable your are dealing with has the base type.
If you still want to access the new implementation, then your code using p1 has to cast it to the correct derived type RightTriangle:
var w = ((RightTriangle)p1).sideA; // Gets correct value.
So what's the problem?
As you may have already noticed now, using new is not a good solution. The code that uses all kinds of Points has to know whether a specific derivate of Point implements a field with new or not. Moreover it has to know, when it receives a Point instance what kind of instance it acutally is underneath. This will lead to lots of very elaborate if-else statements that check what kind of point p1 is being dealt with and run the appropriate behvaiour.
Using virtual and override alleviates the last problem, but only if the base class implements all methods and properties any derived class implements. Which is basically kind of insane. You tried that and I'm sure you noticed on the way that it doesn't make much sense to give a Point a sideA and sideB and a radius. How could Point ever implement those properties meaningfully? And you cannot make them abstract either, because then a Circle also had to implement a sideA and so on.
So how to solve it in a better way?
Think about what you want to do whith this differnet point instances. You collect them together in a list for a reason: they have something in common. Also you iterate over this list doing something with each one of them for a specific reason as well. But what is it you are trying to do exactly? Can you describe it in an abstract way?
Maybe you are getting side lengths and radiuses to calculate the area? Then give Point a virtual method GetArea() and override it in each derived class. A method GetArea() makes sense on all derived types, although it is implemented differently in each one of them.
You could also make GetArea() an abstract method instead of a virtual one. This means it is not implemented in the base class at all and all derived types are forced to implement it on their own.
Maybe you don't want to handle all Points in the list but the RightTriangles only? Then the code doing this should only receive the RightTriangles:
public void HandleRightTriangles(IEnumerable<RightTriangle> rts)
{
// Can work with sideA and sideB directly here, because we know we only got triangles.
}
Call it with:
// using System.Linq;
HandleRightTriangles(objs.OfType<RightTriangle>());