C# Primitive Types - Part 2- Integral numeric types - Byte & SByte

C# Primitive Types - Part 2- Integral numeric types - Byte & SByte

Integral numeric types

Represents integer numbers, all support arithmetic, bitwise logical, comparison and equality operators.

Byte & SByte

Byte is a struct ( So are integers, floating-point types (float, decimal, double), boolean, char, DateTime.) representing an 8-bit unsigned integer. The fact that it is a struct makes it immutable (you can't change its value, you can create a new one with a new value). Byte has two main constant properties MinValue = 0 and MaxValue = 255. On another hand, SByte represents values between -128 and 127.

The most common use of Byte that I came across is to load and manipulate files. Basically, you load a file into the byte array, or to draw images pixel by pixel.

Here we have a funny use of byte array in C++:

But what is a struct?
It's a value type that contains and encapsulates data and related behavior. Usually used for small types oriented on value. If you are looking for something oriented on functionality, definitely use a class.

Value type VS Reference type

Value types have value semantics, reference types have reference semantics. Let me show you an example.

We have the following BarCode struct:

public struct BarCodeStruct
{
    public string Country ;
    public int Code;
    public char Type;

    public BarCodeStruct(string country, int code, char type) 
        => (Country, Code, Type) = (country, code, type);

    public override string ToString() => $"({Country}{Code}{Type})";
}

Using this in our method TestStruct

private static void TestStruct(){
        var country = "PL";
        var barCode1 = new BarCodeStruct(country, 123456789, 'A');
        var barCode2 = barCode1;
        barCode2.Type = 'B';
        Console.WriteLine($"{nameof(barCode1)}: {barCode1}");
        Console.WriteLine($"{nameof(barCode2)}: {barCode2}");

        Change(barCode2);
        Console.WriteLine($"{nameof(barCode2)} after a method call for struct: {barCode2}");
    }

And our Change() method

After running our code we can see in our console that passing the argument into the Change() method created a NEW INSTANCE of BarCodeStruct. barCode2 was not changed, and Change() operated on its local copy.

Let's create a similar code but using the class (reference type)

public class BarCodeClass
{
    public string Country ;
    public int Code;
    public char Type;

    public BarCodeClass(string country, int code, char type) 
        => (Country, Code, Type) = (country, code, type);

    public override string ToString() => $"({Country}{Code}{Type})";
}

and TestClass():

private static void TestClass(){
        var country = "PL";
        var barCode1 = new BarCodeClass(country, 123456789, 'A');
        var barCode2 = barCode1;
        barCode2.Type = 'B';
        Console.WriteLine($"{nameof(barCode1)}: {barCode1}");
        Console.WriteLine($"{nameof(barCode2)}: {barCode2}");

        Change(barCode2);
        Console.WriteLine($"{nameof(barCode2)} after a method call for class: {barCode2}");
    }

which creates this output:

When we passed barCode2 into Change() it used its reference to barCode2, so when changing its value we can see that mutation worked outside the method.

Understanding Integral Numeric Types in C#

Integral numeric types are fundamental in C#, representing whole numbers and supporting operations like arithmetic, bitwise logic, comparison, and equality. In this post, we explore the characteristics and behavior of key types such as byte and sbyte.

Key Highlights

  • byte and sbyte: Represent 8-bit integers with ranges of 0–255 (byte) and -128–127 (sbyte), respectively. These are immutable structs, commonly used in scenarios like file manipulation and image processing.

  • Structs and Immutability: Integral types like byte are structs, meaning they are value types and inherently immutable. This immutability ensures that their values cannot change after creation, only replaced with new ones.

  • Value Types vs. Reference Types: Using examples of BarCodeStruct (a struct) and BarCodeClass (a class), the post demonstrates how these types behave differently when passed to methods or assigned. Value types are copied by value, creating new instances, while reference types are passed by reference, allowing changes to affect the original instance.

By understanding these integral types and their behavior, developers can make better decisions about memory management, data encapsulation, and designing efficient and robust C# applications.