Back to: C# New Features Tutorials
InlineArray Attribute in C# with Examples
In this article, I will discuss the InlineArray Attribute in C# with Examples. Please read our previous article discussing Collection Expressions in C# with Examples. The [InlineArray] attribute in C# 12 specifies a fixed-length array inside a struct. This feature helps you define inline arrays of a fixed size, with the length defined at compile time, optimizing memory and performance. It’s particularly useful when you need a small fixed-length array that is part of a struct.
What is the [InlineArray] Attribute?
The [InlineArray] attribute is added to provide better support for fixed-length arrays within structs. With this attribute, the compiler has more information to optimize memory and make working with arrays inside structs easier. The attribute enforces that the array has a fixed length and cannot be resized or reallocated dynamically. The length parameter is specified at the time of defining the struct.
Syntax of the [InlineArray] Attribute:
[InlineArray(15)] public struct MyStruct { public int[] ArrayField; }
Key Features of the [InlineArray] Attribute:
- Inline Arrays in Structs: Allows structs to hold arrays of a fixed length efficiently.
- Memory Optimized: Since the size is fixed, inline arrays are optimized for memory usage and better performance.
- Compiler Optimizations: Helps the compiler optimize the layout of structs containing inline arrays.
- No Dynamic Resizing: The arrays cannot be resized at runtime, and their length must be specified at compile time.
Example Without [InlineArray(3)] Attribute
Without the [InlineArray] attribute, we need to define a struct that contains an array for coordinates. This approach works, but it has the potential for performance inefficiency.
using System; namespace CSharp12NewFeatures { // Define a struct with a fixed-size array public struct Point { // Array of size 3 to represent coordinates (x, y, z) public int[] Coordinates; // Constructor to initialize the coordinates (x, y, z) public Point(int x, int y, int z) { Coordinates = new int[3] { x, y, z }; } // Method to display the coordinates public void DisplayCoordinates() { Console.WriteLine($"Point Coordinates: X = {Coordinates[0]}, Y = {Coordinates[1]}, Z = {Coordinates[2]}"); } } public class Program { static void Main() { // Create a Point struct and initialize coordinates var point = new Point(1, 2, 3); // Display the coordinates of the point point.DisplayCoordinates(); } } }
Drawbacks of Not Using [InlineArray] Attribute
- Memory Allocation Overhead: In this example, the Coordinates array is dynamically allocated using the new int[3] syntax, which requires allocating memory for the array object itself and the elements inside it. This is inefficient when you know the array size is fixed at compile time (in this case, it’s always 3).
- Potential Garbage Collection Pressure: Since we’re using dynamic arrays, there’s the potential for memory allocations and deallocations at runtime. This leads to more pressure on the garbage collector (GC) to clean up these temporary arrays when the Point object is no longer in use.
- Lack of Clear Intent: Using a new int[3] array in the constructor is not as expressive or intentional as directly stating that the array is always of a fixed length. The [InlineArray] attribute provides clarity to the code, making it clear that the array is fixed-length (3) and should be treated as such.
Example: Using [InlineArray] in Structs
Let’s see how the [InlineArray(3)] attribute can help improve the code and address these issues.
using System.Runtime.CompilerServices; namespace CSharp12NewFeatures { // Define a struct with an inline array of fixed length using [InlineArray] attribute [InlineArray(3)] // Specify that the array should always have a length of 3 (for x, y, z coordinates) public struct Point { // Inline array of fixed length (x, y, z) public int[] Coordinates; // Constructor to initialize the coordinates (x, y, z) public Point(int x, int y, int z) { Coordinates = new int[] { x, y, z }; // Initialize the array with the 3 fixed coordinates } // Method to display the coordinates public void DisplayCoordinates() { Console.WriteLine($"Point Coordinates: X = {Coordinates[0]}, Y = {Coordinates[1]}, Z = {Coordinates[2]}"); } } public class Program { static void Main() { // Create a Point struct with the inline array var point = new Point(1, 2, 3); // Display the coordinates of the point point.DisplayCoordinates(); } } }
Code Explanation:
- Struct Declaration: The Point struct is marked with the [InlineArray(3)] attribute, which specifies that the Coordinates array will always contain three elements (x, y, and z).
- Inline Array: The array Coordinates are defined inside the struct. It automatically has a fixed size of 3 due to the [InlineArray(3)] attribute. This array will store the coordinates of a 3D point.
- Constructor: The constructor takes x, y, and z as parameters and initializes the Coordinates array with these values.
- Displaying Coordinates: The DisplayCoordinates method is used to print the x, y, and z coordinates from the Coordinates array.
How [InlineArray(3)] Improves the Code:
- Memory Allocation Efficiency: By using [InlineArray(3)], we make the array a part of the struct with a fixed size (3 elements). The array is now implicitly part of the struct’s memory layout, eliminating the need for an additional heap allocation and improving memory efficiency. The memory is allocated inline with the struct itself.
- Reduced Garbage Collection Pressure: Since inline arrays are part of the struct and are not dynamically allocated on the heap, the garbage collector’s pressure is reduced. The array elements are now managed alongside the struct, and no additional allocations or deallocations are required for the array itself.
- Clearer Intent: The [InlineArray(3)] attribute explicitly communicates the intent that the Coordinates array is always fixed in size (3). It makes the code easier to understand and conveys the design of the struct more clearly.
Real-Time Example – Game Development
In game development, we often need to represent 3D positions or coordinates for game objects. These positions could represent player locations, enemy locations, or static objects in the game world. The goal of this example is to use the [InlineArray] attribute to define the position of a game object in the form of 3D coordinates (x, y, z). We will make the example more complex by adding functionality for movement, distance calculation, and object management.
using System; using System.Runtime.CompilerServices; namespace GameDevelopmentExample { // Define a struct with an InlineArray for the 3D position (x, y, z) [InlineArray(3)] // Inline array with 3 fixed elements (x, y, z) public struct GameObject { public int[] Position; // Inline array to hold the position coordinates public GameObject(int x, int y, int z) { Position = new int[] { x, y, z }; } // Method to move the game object by a specified vector public void Move(int deltaX, int deltaY, int deltaZ) { Position[0] += deltaX; // Move along the X-axis Position[1] += deltaY; // Move along the Y-axis Position[2] += deltaZ; // Move along the Z-axis } // Method to calculate the distance from another game object public double CalculateDistance(GameObject other) { int dx = Position[0] - other.Position[0]; int dy = Position[1] - other.Position[1]; int dz = Position[2] - other.Position[2]; return Math.Sqrt(dx * dx + dy * dy + dz * dz); // 3D Euclidean distance formula } // Method to display the position of the game object public void DisplayPosition() { Console.WriteLine($"Position: X = {Position[0]}, Y = {Position[1]}, Z = {Position[2]}"); } } // Define a GameWorld that manages multiple game objects public class GameWorld { private GameObject[] gameObjects; public GameWorld(int size) { gameObjects = new GameObject[size]; } // Add a new game object to the world public void AddGameObject(int index, GameObject obj) { if (index >= 0 && index < gameObjects.Length) { gameObjects[index] = obj; } } // Display all game objects' positions public void DisplayAllObjects() { foreach (var obj in gameObjects) { obj.DisplayPosition(); } } // Calculate the distance between two game objects public double CalculateDistanceBetweenObjects(int index1, int index2) { if (index1 >= 0 && index1 < gameObjects.Length && index2 >= 0 && index2 < gameObjects.Length) { return gameObjects[index1].CalculateDistance(gameObjects[index2]); } return 0.0; } } public class Program { static void Main() { // Create game world with space for 3 game objects GameWorld world = new GameWorld(3); // Create and add game objects to the world GameObject player = new GameObject(0, 0, 0); // Player at origin GameObject enemy = new GameObject(10, 0, 0); // Enemy 10 units along X-axis GameObject item = new GameObject(0, 5, 0); // Item 5 units along Y-axis world.AddGameObject(0, player); world.AddGameObject(1, enemy); world.AddGameObject(2, item); // Display initial positions of game objects Console.WriteLine("Initial Positions:"); world.DisplayAllObjects(); // Move the player player.Move(5, 5, 0); // Move player 5 units on X and Y axes // Display updated positions Console.WriteLine("\nPositions after moving the player:"); world.DisplayAllObjects(); // Calculate the distance between the player and the enemy double distance = world.CalculateDistanceBetweenObjects(0, 1); Console.WriteLine($"\nDistance between player and enemy: {distance} units"); // Calculate the distance between the player and the item distance = world.CalculateDistanceBetweenObjects(0, 2); Console.WriteLine($"Distance between player and item: {distance} units"); } } }
Code Explanation:
- GameObject Struct: The GameObject struct uses the [InlineArray(3)] attribute to specify that the Position field holds a fixed-size array of length 3 (representing the x, y, and z coordinates of the object). The Move method allows the game object to be moved by modifying the values of the inline array (Position[0], Position[1], Position[2]). The CalculateDistance method computes the distance between two GameObject instances using the Euclidean distance formula.
- GameWorld Class: The GameWorld class manages multiple game objects, allowing you to add new objects, display their positions, and calculate distances between objects.
- Program Class: The Program class simulates a small game world with three objects: the player, an enemy, and an item. It demonstrates how you can move a game object, calculate distances, and display positions.
Output:
The [InlineArray] attribute introduced in C# 12 allows you to define inline arrays with a fixed length directly within structs. This feature is particularly useful for performance-sensitive applications where fixed-size arrays need to be part of a value type. By using [InlineArray], you can ensure better memory utilization, simplify array management, and optimize your application for scenarios that require small, static arrays.
In the next article, I will discuss Default Parameters in Lambda Expressions in C# with Examples. In this article, I explain the InlineArray Attribute in C# with Examples. I want your feedback. Please post your feedback, questions, or comments about this article.