Skip to main content

2.3 - Type Conversion (Casting)

In programming, you'll often need to convert a value from one data type to another. This process is called type conversion or casting. C# provides several ways to perform these conversions, which we'll explore in this section.

Why Type Conversion is Necessary

Type conversion is needed in many scenarios:

  • When you receive input from users (typically as strings) and need to convert it to numeric types
  • When performing calculations that involve different numeric types
  • When working with different APIs that expect specific data types
  • When storing or retrieving data from external sources like files or databases

Types of Conversions

C# supports two main types of conversions:

  1. Implicit Conversions: Happen automatically when there's no risk of data loss
  2. Explicit Conversions: Require explicit code and may involve potential data loss

Implicit Conversions

Implicit conversions occur automatically when:

  • The conversion is always safe (no data loss)
  • The target type can always hold the full range of the source type

Common implicit conversions include:

  • int to long (a smaller integer type to a larger one)
  • float to double (a less precise floating-point to a more precise one)
  • Any numeric type to decimal
  • Derived class to base class (we'll cover this in Module 5)
// Implicit conversions
int playerLevel = 10;
long playerExperience = playerLevel; // int to long, no cast needed

float itemWeight = 2.5f;
double preciseWeight = itemWeight; // float to double, no cast needed

// In game development context
int damageAmount = 50;
float damageWithMultiplier = damageAmount; // int to float
damageWithMultiplier *= 1.5f; // Now 75.0f

Explicit Conversions (Casting)

Explicit conversions require a cast operator and are needed when:

  • There's potential for data loss
  • The conversion might fail at runtime
  • The compiler needs explicit confirmation that you understand the risks

Common explicit conversions include:

  • long to int (a larger integer type to a smaller one)
  • double to float (a more precise floating-point to a less precise one)
  • float or double to any integer type
  • Base class to derived class (covered in Module 5)
// Explicit conversions using cast operator
double highPrecisionValue = 1234.5678;
float lowerPrecisionValue = (float)highPrecisionValue; // Potential precision loss

long largeNumber = 2147483648; // Too large for int
int smallerNumber = (int)largeNumber; // Results in -2147483648 (overflow)

// In game development context
float playerPositionX = 123.45f;
int gridPositionX = (int)playerPositionX; // Truncates to 123
caution

Explicit conversions can lead to data loss or unexpected results:

  • Converting from floating-point to integer truncates the decimal portion
  • Converting to a smaller numeric type can cause overflow if the value is too large
  • Always consider the potential consequences of explicit conversions

The Convert Class

The System.Convert class provides methods for converting between various base types:

// Using Convert methods
string scoreText = "100";
int score = Convert.ToInt32(scoreText); // String to int

bool isGameOver = Convert.ToBoolean(0); // Numeric to boolean (0 is false)

float healthPercentage = 0.75f;
int healthDisplay = Convert.ToInt32(healthPercentage * 100); // 75

// Convert handles rounding differently than casting
double value = 10.7;
int rounded = Convert.ToInt32(value); // 11 (rounds to nearest integer)
int truncated = (int)value; // 10 (truncates decimal portion)

The Convert class is useful because:

  • It handles null values gracefully (usually returning a default value)
  • It performs rounding rather than truncation for floating-point to integer conversions
  • It provides a consistent API for converting between many different types

Parse and TryParse Methods

For converting strings to other types, most built-in types provide Parse and TryParse methods:

Parse Method

The Parse method converts a string to a specific type:

string healthText = "100";
int health = int.Parse(healthText);

string speedText = "12.5";
float speed = float.Parse(speedText);

string isActiveText = "true";
bool isActive = bool.Parse(isActiveText);

However, Parse throws an exception if the conversion fails, which can crash your program.

TryParse Method

The TryParse method is safer because it returns a boolean indicating success or failure, rather than throwing an exception:

string userInput = "42";
int value;

if (int.TryParse(userInput, out value))
{
// Conversion succeeded, use the value
Console.WriteLine($"Parsed value: {value}");
}
else
{
// Conversion failed
Console.WriteLine("Invalid input. Please enter a number.");
}

The TryParse method is particularly useful for handling user input, where invalid data is common.

Type Conversion in Game Development

Let's look at some common type conversion scenarios in game development:

Example 1: Processing User Input

// Getting and validating user input for game settings
Console.WriteLine("Enter difficulty level (1-5):");
string input = Console.ReadLine();
int difficultyLevel;

if (int.TryParse(input, out difficultyLevel) && difficultyLevel >= 1 && difficultyLevel <= 5)
{
// Valid input
SetGameDifficulty(difficultyLevel);
}
else
{
// Invalid input
Console.WriteLine("Invalid difficulty level. Using default (3).");
difficultyLevel = 3;
SetGameDifficulty(difficultyLevel);
}

Example 2: Converting Between Game Units

// Converting between different game measurement units
float metersPosition = 150.0f;

// Convert to centimeters (1m = 100cm)
int centimetersPosition = (int)(metersPosition * 100);

// Convert to game tiles (assuming each tile is 2.5 meters)
int tilePosition = (int)(metersPosition / 2.5f);

// Convert to a percentage of the total map size (assuming map is 1000 meters)
float mapProgressPercentage = (metersPosition / 1000.0f) * 100.0f;
string progressText = $"Progress: {mapProgressPercentage:F1}%";

Example 3: Working with Different Numeric Representations

// Health as a percentage and as a fraction
int maxHealth = 100;
int currentHealth = 75;

// Convert to percentage for UI display
float healthPercentage = (float)currentHealth / maxHealth * 100f;
string healthText = $"Health: {healthPercentage:F0}%";

// Convert to a 0-1 range for a health bar fill amount
float healthBarFill = (float)currentHealth / maxHealth;

// Convert RGB color values (0-255) to Unity's 0-1 range
byte redComponent = 200;
byte greenComponent = 100;
byte blueComponent = 50;

float normalizedRed = redComponent / 255f; // 0.784
float normalizedGreen = greenComponent / 255f; // 0.392
float normalizedBlue = blueComponent / 255f; // 0.196

Boxing and Unboxing

C# is a type-safe language with a unified type system where all types derive from System.Object. This enables two special conversion operations:

Boxing

Boxing is the process of converting a value type to a reference type by wrapping it in an object:

int score = 100;
object boxedScore = score; // Boxing (implicit)

Unboxing

Unboxing is the reverse process—extracting the value type from an object:

object boxedValue = 100;
int unboxedValue = (int)boxedValue; // Unboxing (explicit)
caution

Boxing and unboxing operations have performance implications because they involve memory allocation and type checking. In performance-critical code, especially in games, it's best to minimize these operations.

Type Conversion Best Practices

  1. Use implicit conversions when possible for better performance and safety.

  2. Prefer TryParse over Parse for user input to avoid exceptions.

  3. Be aware of potential data loss when converting between types.

  4. Check for overflow when converting between numeric types of different sizes.

  5. Consider using appropriate data types from the start to minimize the need for conversions.

  6. Avoid unnecessary boxing and unboxing for performance reasons.

  7. Use explicit casts when you're certain the conversion is safe and understand the implications.

Conclusion

Type conversion is an essential skill in C# programming. By understanding the different conversion methods and their implications, you can write more robust code that handles data appropriately.

In the next section, we'll explore console input and output, where you'll apply your knowledge of type conversion to process user input effectively.

Unity Relevance

In Unity development, type conversion is frequently used for:

  • Converting user input to appropriate game values
  • Transforming between different measurement units (pixels, world units, percentages)
  • Converting between Unity's data types (Vector3, Quaternion) and basic types
  • Parsing data from external sources like save files, configuration files, or network messages
  • Converting between different color spaces and representations

Understanding type conversion helps you handle these scenarios correctly and avoid subtle bugs in your Unity projects.