The meaning of life is to explore the world

Dependency update mechanism

Posted on By Jason Liu

Problem:

// Create an Image class with two properties, Width and Height
// Whenever Width is updated, Height should be updated in proportion
// Tips: avoid below 2 traps
// 1. Updating Width and Height triggers an infinite dead loop
// 2. The dependent update triggered incorrectly by the object initialization or copy

Solution 1:

class Image1
{
	//private bool dependentUpdateLock;
	int _width;
	public int Width
	{
		get => _width;
		set
		{
			if (_width != 0)
				_height = (int)Math.Round(1.0 * value * _height / _width);
			_width = value;
		}
	}
	int _height;
	public int Height
	{
		get => _height;
		set
		{
			if (_height != 0)
				_width = (int)Math.Round(1.0 * value * _width / _height);
			_height = value;
		}
	}
	public Image1(int w, int h)
	{
		_width = w;
		_height = h;
	}
	public void Display()
	{
		Console.WriteLine(string.Format("W=={0}, H=={1}", Width, Height));
	}
}

Solution 2:

class Image2
{
	public int Width { get; private set; }
	public int Height { get; private set; }
	public void SetWidthAndHeight(int w, int h)
	{
		Width = w;
		Height = h;
	}
	public int NewWidthFromNewHeight(int newHeight)
	{
		return Height == 0 ? Width : (int)Math.Round(1.0 * newHeight * Width / Height);
	}
	public int NewHeightFromNewWidth(int newWidth)
	{
		return Width == 0 ? Height : (int)Math.Round(1.0 * newWidth * Height / Width);
	}
	public Image2(int w, int h)
	{
		SetWidthAndHeight(w, h);
	}
	public void Display()
	{
		Console.WriteLine(string.Format("W=={0}, H=={1}", Width, Height));
	}
	public Image2 Clone()
	{
		return new Image2(Width, Height);
	}
}

Result in comparison:

void Test_Dependent_Update()
{
	Image1 image1A = new Image1(10, 10);
	Console.WriteLine("A:");
	image1A.Display();
	Image1 image1B = new Image1(15, 5);
	Console.WriteLine("B:");
	image1B.Display();
	image1A = image1B;
	Console.WriteLine("A = B:");
	image1A.Display();
	image1A.Width = 10;
	Console.WriteLine("A after W explicitly set:");
	image1A.Display();
	image1A.Height = 10;
	Console.WriteLine("A after H explicitly set:");
	image1A.Display();
	Console.WriteLine("================");
	Image2 image2A = new Image2(10, 10);
	Console.WriteLine("A:");
	image2A.Display();
	Image2 image2B = new Image2(15, 5);
	Console.WriteLine("B:");
	image2B.Display();
	image2A = image2B.Clone();
	Console.WriteLine("A = B.Clone():");
	image2A.Display();
	image2A.SetWidthAndHeight(10, 10);
	Console.WriteLine("A after W&H set separately:");
	image2A.Display();
	image2B.SetWidthAndHeight(10, image2B.NewHeightFromNewWidth(10));
	Console.WriteLine("B after W&H set in relation:");
	image2B.Display();
}

Output:

A:
W==10, H==10
B:
W==15, H==5
A = B:
W==15, H==5
A after W explicitly set:
W==10, H==3
A after H explicitly set:
W==33, H==10
================
A:
W==10, H==10
B:
W==15, H==5
A = B.Clone():
W==15, H==5
A after W&H set separately:
W==10, H==10
B after W&H set in relation:
W==10, H==3