This documentation is archived and is not being maintained.

Value Types in the Common Type System

Most programming languages provide built-in data types, such as integers and floating-point numbers that are copied when they are passed as arguments (that is, they are passed by value). In the .NET Framework, these are called value types. The runtime supports two kinds of value types:

  • Built-in value types

    The .NET Framework defines built-in value types, such as System.Int32 and System.Boolean, which correspond and are identical to primitive data types used by programming languages.

  • User-defined value types

    Your language will provide ways to define your own value types, which derive from System.ValueType or System.Enum. If you want to define a type representing a value that is small, such as a complex number (using two floating-point numbers), you might choose to define it as a value type because you can pass the value type efficiently by value. If the type you are defining would be more efficiently passed by reference, you should define it as a class instead.

For information specific to enumerations, see Enumerations in the Common Type System.

Value types are stored as efficiently as primitive types, yet you can call methods on them, including the virtual methods defined on the System.Object and System.ValueType classes, as well as any methods defined on the value type itself. You can create instances of value types, pass them as parameters, store them as local variables, or store them in a field of another value type or object. Value types do not have the overhead associated with storing an instance of a class and they do not require constructors.

For each value type, the runtime supplies a corresponding boxed type, which is a class that has the same state and behavior as the value type. Some languages require you to use special syntax when the boxed type is required; others automatically use the boxed type when it is needed. When you define a value type, you are defining both the boxed and the unboxed type.

Value types can have fields, properties, and events. They can also have static and nonstatic methods. When they are boxed, they inherit the virtual methods from System.ValueType, and they can implement zero or more interfaces.

Value types are sealed, which means that no other type can be derived from them. However, you can define virtual methods directly on the value type, and these methods can be called on either the boxed or unboxed form of the type. Although you cannot derive another type from a value type, you might define virtual methods on a value type when you are using a language in which it is more convenient to work with virtual methods than with nonvirtual or static methods.

The following example shows how to construct a value type for complex numbers.

using System;

// Value type definition for a complex number representation.
public struct Complex
    public double r, i;

    // Constructor.
    public Complex(double r, double i) { this.r = r; this.i = i; }

    // Returns one divided by the current value.
    public Complex Reciprocal
            if (r == 0d && i == 0d)
                throw new DivideByZeroException();

            double div = r*r + i*i;
            return new Complex(r/div, -i/div);

    // Conversion operators.
    public static explicit operator double(Complex a)
        return a.r;
    public static implicit operator Complex(double r)
        return new Complex(r,0d);

    // Basic unary operators.
    public static Complex operator + (Complex a)
        return a;
    public static Complex operator - (Complex a)
        return new Complex(-a.r, -a.i);

    // Basic binary operators for addition, subtraction, multiplication, and division.
    public static Complex operator + (Complex a, Complex b)
        return new Complex(a.r + b.r, a.i + b.i);
    public static Complex operator - (Complex a, Complex b)
        return new Complex(a.r - b.r, a.i - b.i);
    public static Complex operator * (Complex a, Complex b)
        return new Complex(a.r*b.r - a.i*b.i, a.r*b.i + a.i*b.r);
    public static Complex operator / (Complex a, Complex b)
        return a * b.Reciprocal;

    // Override the ToString method so the value appears in write statements.
    public override string ToString() {
        return String.Format("({0}+{1}i)", r, i);

// Entry point.
public class ValueTypeSample
    public static void Main()
        Complex a = new Complex(0, 1);
        Complex b = new Complex(0, -2);

        Console.WriteLine("a = " + a);
        Console.WriteLine("b = " + b);

        Console.WriteLine("a + b = " + (a+b));
        Console.WriteLine("a - b = " + (a-b));
        Console.WriteLine("a * b = " + (a*b));
        Console.WriteLine("a / b = " + (a/b));

        Console.WriteLine("(double)a = " + (double)a);
        Console.WriteLine("(Complex)5 = " + (Complex)5);

The output from this program is as follows.

a = (0+1i)
b = (0+-2i)

a + b = (0+-1i)
a - b = (0+3i)
a * b = (2+0i)
a / b = (-0.5+0i)

(double)a = 0
(Complex)5 = (5+0i)