The title of the question may seem confusing but bear with me, I'll try to explain the problem as clearly as possible.
So I was just studying about the Liskov substitution principle from a course and the lecturer has given an example showing logical errors we can solve using this principle. Hence, the example shown below is the problem with the logical error.
(Note: Please read the whole question even if you don't know/find out this example has nothing to do with Liskov principle as I've mentioned that above. I've just kept this in question for reference, just in case someone bothers to answer that you're doing it the wrong way)
Rectangle is the parent class
class Rectangle
{
public int Width { get; set; }
public int Height { get; set; }
public Rectangle()
{
}
public Rectangle(int width, int height)
{
Width = width;
Height = height;
}
public override string ToString()
{
return $"{nameof(Width)}: {Width}, {nameof(Height)}: {Height}";
}
}
Square class
class Square : Rectangle
{
public new int Width
{
set { base.Width = base.Height = value; }
}
public new int Height
{
set { base.Width = base.Height = value; }
}
}
Just a simple Caller
private void Caller()
{
Rectangle rc = new Square(); //Upcasting here
rc.Width = 4;
Console.WriteLine($"{rc}"); //Now here the o/p is **Width: 4, Height: 0** which is correct
//But when we use object initializer as shown below
Rectangle rcTwo = new Square { Width = 4 };
Console.WriteLine($"{rcTwo}"); //the o/p is **Width: 4, Height: 4**
}
Now aren't we just initializing the object in a different way? Why is the O/P supposed to differ in that case. I thought that object initializer is just an syntactical sugar when when we to create an object and initialize it's properties in comparison with the traditional approach.
You are right that it's syntactical sugar. But you have to think of the order of operations: assignment is done last, after all the operations on the right of the equals sign. So your second version with initialiser is the same as:
Square square = new Square();
square.Width = 4;
Rectangle rcTwo = square;
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
The following example works fine, but it has a flaw: the Square object has the Width and Height properties of the Rectangle. A good implementation of the Square object won't provide these properties to the user:
public class Rectangle
{
public double Width { get; set; }
public double Height { get; set; }
public double Area { get { return Width * Height; } }
}
public class Square : Rectangle
{
public double Side
{
get { return Width; }
set { Width = Height = value; }
}
}
I thought of a few ways to fix that (including new properties and the Obsolete tag). The best solution was to cancel the inheritance, and instead keep a Rectangle as a private variable of the Square object. This would work, but will require re-writing the Rectangle methods for Square, which is against the whole purpose of OOP. Please help me understand what is the right way to implement this.
Thanks
I don't agree that Square shouldn't provide Width and Height properties since squares absolutely do have a width and height just like any rectangle. The only difference is that they are necessarily the same.
The way I'd fix it, is to either make a Rectangle immutable, or allow Square to override the properties:
public class Rectangle
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public Rectangle(int width, int height)
{
Width = width;
Height = height;
}
}
public class Square : Rectangle
{
public override int Width
{
get { return Side; }
set { Side = value; }
}
public override int Height
{
get { return Side; }
set { Side = value; }
}
public int Side { get; set; }
public Square(int side)
: base(side, side)
{
Side = side;
}
}
That's a common problem that some people answer with "OOP is not the silver bullet". But if you really want to use OOP mechanisms here, you could override the Width to always be the Height for a square. So if you set one, the other changes as well. Not perfect, but it works.
public class Rectangle
{
public virtual double Width { get; set; }
public virtual double Height { get; set; }
public double Area { get { return Width * Height; } }
}
public class Square : Rectangle
{
public override double Width { get { return Height; } set { Height = value; } }
}
Don't inherit Square from Rectangle. You are trying to have the relation between your abstractions same as the relation between objects which they represent. But think about following
When the spouses are getting divorced each one of them has the lawyer which represents them. It's very unlikely that those two lawyers themselves are getting divorced. Because the representatives of things do not share the relationships of things they represent © Uncle Bob
Your Square class represents square geometric shape. But it's not a geometric shape. It's a code. Same with Rectangle class. And relationship between geometric shapes are not shared by these representatives. Let's talk about problems which you will have if you will try to share relationship between geometric square and rectangle with Square and Rectangle classes in your code.
If you inherit Square from Rectangle then you can treat any square as base class:
var rectangle = new Square { Side = 10 };
rectangle.Width = 20;
Now you have 'square' object with width 20 and height 10. Did you expect to have such square? OK, you can synchronize those properties. I.e. when you change width, height will change as well and vice versa:
rectangle.Width = 5;
rectangle.Height = 10;
Assert.That(rectangle.Area, Is.EqualTo(50)); // wtf it fails?
Did you expect that height also will be 5 if you are using variable of base type (rectangle)? It's not valid behavior of rectangle. And it violates Liskov Substitution Principle.
If you will not inherit Square from Rectangle - none of those problems will appear. Square will have single property Side and it will behave as you expect. The only case when you might inherit Square from Rectangle without having unexpected values of width or height - if make those object immutable. But another problem will stay - your square still will have properties Width and Height which are confusing. It should have only one property Side.
Setting the Rectangle properties to virtual will allow you to override their behaviors and have square inherit Rectangle.
Please see the example below for more information:
public class Rectangle
{
public virtual double Width { get; set; }
public virtual double Height { get; set; }
public double Area { get { return Width * Height; } }
}
public class Square : Rectangle
{
public double Side { get; set; }
public override double Height
{
get { return Side; }
set { Side = value; }
}
public override double Width
{
get { return Side; }
set { Side = value; }
}
}
namespace Area
{
public class Rectangle
{
private double length;
private double width;
public Rectangle() { }
public Rectangle(double length, double width)
{
this.Length = length;
this.Width = width;
}
public double Length
{
get
{
return length;
}
set
{
length = value;
}
}
public double Width
{
get
{
return width;
}
set
{
width = value;
}
}
public double getArea()
{
return width * length;
}
public double getPerimeter()
{
return 2 * width + 2 * length;
}
public double getDiagonal()
{
return Math.Sqrt(Math.Pow(width, 2) + Math.Pow(length, 2));
}
I want to make sure I am using best practices with C# Objects. Please use the above example for reference.
1. Is it necessary that I type the first empty Constructor? In class the Instructor always did on each program but never really gave an answer as to why.
public Rectangle() { }
2. Inside my Custom Constructor Visual Studio generates it like this:
this.Length = length;
I know that the "this" keyword is not necessary the way it is typed, but in class the instructor sometimes changed it to lowercase like this:
this.length = length;
But sometimes he didn't change it. Which way is best practices?
And is the left side the actual Property? And then the right side is the field?
So it is, Property equals field?
3. And finally, in this case cant I just type my properties as:
public string Length { get; set; }
instead of the way Visual Studio generates with the return and value.
Sorry for the long post, I am tired of getting different answers at school and want one final answer on this, thanks.
I would suggest that your class look like this:
public class Rectangle
{
public Rectangle(double length, double width)
{
this.Length = length;
this.Width = width;
}
public double Length { get; set; }
public double Width { get; set; }
public double Area { get { return this.Width * this.Length; } }
public double Perimeter { get { return 2.0 * (this.Width + this.Length); } }
public double Diagonal { get { return Math.Sqrt(Math.Pow(this.Width, 2.0) + Math.Pow(this.Length, 2.0)); } }
}
See here for why you might want a blank constructor. To summarize, adding a non blank constructor will stop the compiler from generating a blank one for you (the compiler assumes that if you wanted it, you would have defined it with the other constructors you wrote). Some things, like serialization, will not work without a blank constructor, so that's a reason you might want to add one.
In my career, I've mostly seen people avoid using this in constructors. Maybe avoid isn't the right word, but unless it's unclear, they just didn't bother to put it there. This is probably too minor an issue to lose any sleep over.
UPDATE based on some of your comments
When you write
public Rectangle(double length, double width)
{
Length = length; //parameter length assigned to field length by virtue of property Length
}
you are assigning the parameter length to the property Length, which itself assigns the passed in value to the length private field. Since C# is case sensitive, Length and length aren't confused in any scenario, and you don't need to specify the this keyword.
Inside a method with a parameter called length, the language is assuming that you are referring to the parameter of the method when you type length. So if you try to do something like this:
public Rectangle(double length, double width)
{
length = length; //warning: Assignment made to same variable; did you mean to assign to something else
}
The compiler doesn't try and assume that you are assigning the property to the field, and this is just assigning the length parameter to itself. In this case, you would use the this keyword to tell the compiler that you want to assign the parameter length to the private field length, like this:
public Rectangle(double length, double width)
{
this.length = length; //no warning
}
END UPDATE
Yes, you could declare the property as just Property {get;set;}. This feature is only from C# 3.0 and is called auto-implemented properties (see this link). Before that you had to provide the implementation yourself.
I changed my class to this:
public class Rectangle
{
public Rectangle(double length, double width)
{
Length = length;
Width = width;
}
public double Length { get; set; }
public double Width { get; set; }
public double getArea()
{
return Width * Length;
}
public double getPerimeter()
{
return 2 * Width + 2 * Length;
}
public double getDiagonal()
{
return Math.Sqrt(Math.Pow(Width, 2) + Math.Pow(Length, 2));
}
}
If anyone has any other feedback on anything above that you recommend to change please give it, I catch on very fast and want to learn the correct way.
What is the best practice to pass objects as constructor arguments? Passing mutable objects can lead to unexpected results.
A simple example. We expect 200, but get 10000 calling the TestMethod():
public class Test
{
public int TestMethod()
{
var variable1 = new SomeMeasurements
{
Width = 10,
Height = 20
};
var obj1 = new MyRectangle(variable1);
// <... more code...>
variable1.Height = 1000; // a local variable was reused here, and it's field was changed
// <... more code...>
return obj1.GetArea();
}
}
public class SomeMeasurements
{
public int Width { get; set; }
public int Height { get; set; }
}
public class MyRectangle
{
SomeMeasurements _arg;
public MyRectangle(SomeMeasurements arg)
{
_arg = arg;
}
public int GetArea()
{
return _arg.Width * _arg.Height;
}
}
In this case the error is obvious, but with more complex classes debugging can be tedious. Several things how to fix this have crossed my mind:
option 1. Fix TestMethod() - it mustn't change variable1 after creating MyRectangle.
option 2. Fix class SomeMeasurements - turn it into a struct:
public struct SomeMeasurements
{
public int Width { get; set; }
public int Height { get; set; }
}
option 3. Fix class SomeMeasurements - make it immutable:
public class SomeMeasurements
{
public SomeMeasurements(int width, int height)
{
Width = width;
Height = height;
}
public int Width { get; }
public int Height { get; }
}
option 4. Fix class MyRectangle body - it mustn't use mutable objects:
public class MyRectangle
{
int _height;
int _width;
public MyRectangle(SomeMeasurements arg)
{
_height = arg.Height;
_width = arg.Width;
}
public int GetArea()
{
return _width * _height;
}
}
option 5. Make SomeMeasurements ICloneable and use it's Clone() in MyRectangle constructor.
Any of these options have it's flaws - it might be hard to avoid reusing variable1, MyRectangle can be more complex to turn it into a struct, MyRectangle can be external and you might not change it at all, etc. What is the most correct way to fix this?
Generally you should be passing services that conform to a certain interface, or immutable objects only in constructors. The constructor should take a copy of any mutable data passed to it if you want to protect it from external changes.
Once the data goes through the constructor it should be considered part of the new instance's state, and shouldn't be available for modification outside of that instance.
Your options 3,4 seem most useful. Option 2 would fix the problem because you pass a copy of the data into the constructor. Option 1 may be out of your control in many contexts.
It depends on the relationship between the classes, and what they are designed to do.
If you consider a StreamReader class constructed from a Stream instance, that Stream is expected to continue to be "it's own" mutable class with its own set of responsibilities while the reader deals with its mutability in a given way. There is an ongoing relationship between the two objects, and if one does something to the Stream here, one expects it to affect the reader.
In that case we obviously just hold a reference to the Stream passed to the constructor.
In other cases an object passed to represent the initial state of the object being created. There isn't an ongoing relationship between the two.
Here it's best to copy either the object passed or its fields. (When it comes to micro-opts, copying the fields makes the initial construction very slightly slower and the uses of them very slightly faster).
Which case you are dealing with is something that is part of what you are designing, in that you can decide to make a class work either way. Some cases clearly have to be one or the other (in the StreamReader example it would make no sense to ever not hold on to the Stream you were dealing with), but often there is a choice. Favour the principle of least surprise, and if you still can't make up your mind favour the copying approach where there is no ongoing relationship between the objects as your dependencies are now simpler.
This is not exactly what I am working with but I hope it makes a clear example:
public abstract class Shape
{
public int Area;
public int Perimeter;
public class Polygon : Shape
{
public int Sides;
public Polygon(int a, int p, int s){
Area = a;
Perimeter = p;
Sides = s;
}
}
public class Circle : Shape
{
public int Radius;
public Circle(int r){
Area = 3.14*r*r;
Perimeter = 6.28*r;
Radius = r;
}
}
}
In the main function I would have something like this:
Shape[] ThisArray = new Shape[5];
ThisArray[0] = new Shape.Circle(5);
ThisArray[1] = new Shape.Polygon(25,20,4);
My problem is that when I deal with ThisArray, I can't access values other than Area and Perimeter. For example:
if (ThisArray[0].Area > 10)
//This statement will be executed
if (ThisArray[1].Sides == 4)
//This will not compile
How can I access Sides from ThisArray[1]? I could access it if I did something like
Shape.Polygon RandomSquare = new Shape.Polygon(25,20,4) but not if it is in an array of shapes.
If I recall correctly this could be accomplished in C++ by doing something like
Polygon->ThisArray[1].Sides (I forget what this is called) but I do not know how do this in C#
If I can't do what I am trying to do, how can I circumvent this problem?
Thank you for reading through what I intended to be short, any help is appreciated.
You should use casting:
(ThisArray[1] as Shape.Polygon).Sides
Note that you should make sure the underlying object instance actually IS a Polygon, otherwise this will raise an exception. You can do this by using something like:
if(ThisArray[1] is Shape.Polygon){
(ThisArray[1] as Shape.Polygon).Sides
}
I made custom interface system which uses basic UI controls like button, label, etc. Some controls have many options, so they use long constructors, and they only differ in one or two parameters. And this is work in progress, so I change optional parameters a lot, and it takes quite some time to apply changes to all constructors.
public Button(string Text, Rectangle Rect, Texture2D Texture, bool moreStuff)
public Button(string Text, Point Position, Texture2D Texture, bool moreStuff)
public Button(string Text, Vector2 Position, Texture2D Texture, bool moreStuff)
I tried using dynamic keyword instead of Rectangle, Point and Vector2 to decrease the number of constructors, and it compiles, works, and seems ok for the moment. But maybe I'm missing something that might break this approach later?
To find out what was passed as dynamic Position I check for .GetType().Name, use a switch and throw an exception at default: if it wasn't a recognized type.
Is it fine to do it like this, or is there a better (more safe or appropriate) way?
Currently it's possible to create a fully customized instance of Button inline, and I wouldn't like to lose that ability.
You don't need to define constructor arguments if you're finding it tedious. You could use an object initializer quite nicely:
SomeButton button = new SomeButton()
{
Text = "",
MoreStuff = false
};
This calls for a parameter object. That's a class with a property per parameter. Your Button constructor would now only take one parameter: That parameter object.
Using dynamic to reduce the number of overloads is definitely not the correct approach.
Using dynamic is not appropritate in your scenario. Having differenct constructor overloads is not a bad thing (not worth than abusing the dynamic keyword). Many classes in .NET Framework BCL has several constructor overloads (for example, FileStream class has 15 contstructors) and some methods has several overload for differenct uses (MessageBox.Show for example).
Another approach is to type this yourself:
class YourPositioningType {
public int X { get; set; }
public int Y { get; set; }
public static YourPositioningType FromVector(Vector2 vector) {
return new YourPositioningType() { X = vector.X, Y = vector.Y };
}
public static YourPositioningType FromRectangle(Rectangle rect) {
// etc..
}
}
Static methods to convert from each of the above types. Then you would call it as:
Button b = new Button("Blah", YourPositioningType.FromVector(new Vector2() { /* etc */));
Then you just use the above class in a single constructor.
If you use object initialisers, then you can set each individual property you require independently. You just have to be careful that the various properties can be initialised independently.
For example:
public class Button
{
public int Width
{
get
{
return Rectangle.Width;
}
set
{
Rectangle.Width = value;
}
}
public int Height
{
get
{
return Rectangle.Height;
}
set
{
Rectangle.Height = value;
}
}
public int X
{
get
{
return Rectangle.Left;
}
set
{
Rectangle.Left = value;
}
}
public int Y
{
get
{
return Rectangle.Top;
}
set
{
Rectangle.Top = value;
}
}
public Rectangle Rectangle
{
get;
set;
}
}
If you had a class like the above, you can do either:
var button = new Button
{
Rectangle = new Rectangle(...)
}
or:
var button = new Button
{
Left = 0,
Top = 0,
Width = 100,
Height = 20
}
Object initializer notation is ideal for initializing object via their properties where you can have multiple combinations of properties needing to be set.