Not So Complex Numbers in C#
Written by Mike James   
Thursday, 25 November 2021
Article Index
Not So Complex Numbers in C#
Complex Functions

Did you know that .NET supports complex numbers? The Complex struct is easy to use and just needs a little extra publicity so that we all remember it's there!

Not so complex numbers

Support for complex numbers was introduced in C# 4.0 but it's been easy to miss this revolution in C# number-crunching. 

Of course complex numbers don't matter to everyone - in fact you might say that they are a minority interest but they deserve support in any programming language. It is often said that the only real reason scientists still use Fortran is that it has out-of-the-box support for complex numbers where other languages have to retrofit using a package of some kind.


The new features are contained in System.Numerics, but this isn't included automatically in a startup project - yes, complex numbers are always going to be a niche topic. 

To make use of it you have to right click on the References in the Solution Explorer and add System.Numerics. To make using the new features easier also add:

using System.Numerics;

To get started let's create the "hello world" of any numeric program and add two numbers together:

Complex z1 = new Complex(1, 1);
Complex z2 = new Complex(-1, 0);
Complex z3 = z1 + z2;

If you can't see the Output Window make sure you have selected it using Debug,Windows.You should see the result (0,1).

The points to note are - you can create and initialise a complex number using the constructor, the + operator is overloaded to perform complex addition and return a new Complex object as its result and the ToString method returns the number in Cartesian form.

There are other ways to create a Complex object, however. 

For example you can assign a real number to create a Complex number with imaginary component set to zero. For example:

Complex z2 = -1;

is the same as:

Complex z2 = new Complex(-1, 0);

There are implicit and explicit conversion operators for most of the reasonable and common data types. So if it makes sense you can assign a real value to a complex variable.

Notice that the real and imaginary components of Complex are stored as Double. Also notice that the BigInteger struct can be cast to Complex.

However you can't directly assign complex or imaginary quantity. That is z2=(0,1) or any variation on this doesn't work.

The static field ImaginaryOne can be used to create a complex number set equal to i. That is:

Complex z=Complex.ImaginaryOne;

is the same as

Complex z=Complex(0,1);


You can use the Real and Imaginary properties to read the real and imaginary components of a complex number but you cannot use them to set a value.

That is an instruction like:


is illegal.  If you find this surprising you are not alone.




The reason that you can't use the Real and Imaginary properties to modify a Complex object is that it has been designed to be immutable - once created you cannot change it.

Again this too sounds like a surprising choice but immutability is something that is becoming increasingly favoured as a design concept in modern programming language design. To ensure that the Complex type is immutable all of its properties - Real, Imaginary, Magnitude and Phase - are read-only.

The next obvious question is if Complex is immutable how do you use it to do arithmetic where things must change? The answer is that each time assignment occurs a new instance of the Complex object is created. 

For example when you write

Complex z3 = z1 + z2;

the operation of addition creates a new Complex object which z3 is set equal to.That is:

Complex z3 = z1 + z2;

is exactly equivalent to:

Complex z3= new Complex(

Contrast this with the more direct approach of storing the real and imaginary results of performing the addition into the Real and Imaginary properties of z3.

In the same way

Complex z2 = -1;

calls the implicit conversion method which creates an instance of Complex which z2 is set to. That is:

Complex z2 = -1;

is exactly equivalent to:

Complex z2=new Complex(-1,0);

Complex is a struct not a class and this means it follows value semantics - which is what you generally want.

For example, when you pass a Complex type as a parameter it will be passed by value and any changes that you make to the parameter do not alter the original variable. If you want to pass the parameter by reference so that it can be changed simply add ref in front of the parameter.



Last Updated ( Thursday, 25 November 2021 )