Every programming language allows to define and classify data so we can use it appropriately in various parts of the program in the correct form with or without changing the original value or just copying the original value. Any data that we need to use in the program need to be stored in the memory.
A Variable is the name that we give to that particular temporary location in memory where we can store data (eg: lastname, email etc). Now each data or value need to classified as of a particular type eg: integer or decimal or string etc.
We use Data Types to classify data that is called out by a variable name.
Primitive Type is a data type that a programming language provides (eg: int, float, decimal, long, byte, bool, char etc).
Non-primitive Type is a data type which is not provided by programming language, but the programmer creates them (eg: class, arrays, string(C#), struct, enum etc ) which in turn gets used as reference variables (eg: we create class and we later instantiate the object as a reference variable via the new operator). Non-primitive types are essentially referencing the actual memory location which means that the variables that we use to define/call out the non-primitive data (class, array, struct etc) actually refer to a memory location – this means that if we copy these variable we are essentially copying the reference or memory address and then if we modify the value of the copied variable, then value of the original variable will change as well since both variable refer to the same memory location.
Reference Type is a data type that when called out by a variable, creates reference variable, referencing memory location and copies the memory address on the second variable when copied to. Almost all Non-primitive types are Reference Types except struct (in C#).
Value Type is a data type that when called out by a variable and when copied to another variable, just copies the value on to the second variable. All primitive types are value types.