From AS3 to C#, Part 18: Resource Allocation and Cleanup

Datetime:2016-08-23 02:54:40          Topic: ActionScript  C#           Share

Today wecontinue the seriesby looking at how resources—primarily memory—are acquired and cleaned up in C#. We’ll go way beyond the new operator and discuss advanced features like finalizers and using blocks that can make releasing resources much less prone to errors. Read on to learn!

Let’s start off simply with the new operator. It does the same thing in C# as it does in AS3: allocates memory for an instance of a class/struct and calls its constructor. It is by far the most common way to allocate resources in both languages.

// AS3
var p:Point = new Point();
// C#
Point p = new Point();

AS3 has the Array and Vector classes for when you want to allocate a bunch of objects held in a container object. Array holds its elements intwo parts: a densely-packed part of contiguous elements and a sparsely-packed map/dictionary of elements. Vector only has a densely-packed part. It’s somewhat strongly-typed, but there are many exceptions due to it being implemented behind the scenes by four different classes . Here’s how the two look:

var aVals:Array = new Array(); // empty array
var aVals:Array = new Array(5); // array with 5 elements
var aVals:Array = []; // empty array
var aVals:Array = [1, 2, 3, 4, 5]; // array with these 5 elements
 
var vVals:Vector.<int> = new Vector.<int>(); // empty vector
var vVals:Vector.<int> = new Vector.<int>(5); // vector with 5 default elements
var vVals:Vector.<int> = new <int>[]; // empty vector
var vVals:Vector.<int> = new <int>[1, 2, 3, 4, 5]; // vector with these 5 elements
 
var val:int = aVals[3]; // get fourth array element
var val:int = vVals[3]; // get fourth vector element

C#, on the other hand, has strongly-typed arrays of densely-packed values, like AS3′s Vector . You use the square brackets syntax ( [] ) to refer to an array of something and the curly braces ( {} ) syntax to initialize them.

int[] vals = new int[0]; // empty array
int[] vals = new int[5]; // array with 5 default elements
int[] vals = new int[]{}; // empty array
int[] vals = new int[]{1, 2, 3, 4, 5}; // array with these 5 elements (alternate)
int[] vals = {1, 2, 3, 4, 5}; // array with these 5 elements (alternate)
 
int val = vals[3]; // get fourth array element

C# also explicitly supports the concept of multi-dimensional arrays like you’d use for tables or grids. You simply add a comma for each extra dimension:

int[,] vals = new int[2,3]; // 2x3 array of default elements
int[,] vals = new int[2,3]{ {1,2,3}, {4,5,6} }; // 2x3 array of these elements
int[,] vals = new int[,]{ {1,2,3}, {4,5,6} }; // 2x3 array of these elements
int[,] vals = { {1,2,3}, {4,5,6} }; // 2x3 array of these elements
 
int[,,] vals = new int[2,3,4]; // 2x3x4 array of default elements
// 2x3x4 array of these elements
int[,,] vals = new int[2,3,4]{
	{ {1,2,3,4},     {5,6,7,8},     {9,10,11,12}  },
	{ {13,14,15,16}, {17,18,19,20}, {21,22,23,24} }
};
// 2x3x4 array of these elements (alternate)
int[,,] vals = new int[,,]{
	{ {1,2,3,4},     {5,6,7,8},     {9,10,11,12}  },
	{ {13,14,15,16}, {17,18,19,20}, {21,22,23,24} }
};
// 2x3x4 array of these elements (alternate)
int[,,] vals = {
	{ {1,2,3,4},     {5,6,7,8},     {9,10,11,12}  },
	{ {13,14,15,16}, {17,18,19,20}, {21,22,23,24} }
};
 
int val = vals[1,2]; // get array element at (1,2)
int val = vals[1,2,3]; // get array element at (1,2,3)

This is in contrast to AS3′s approach to multi-dimensional arrays which is to nest arrays/vectors within arrays/vectors:

// 2x3 array of default elements
var vals:Vector.<Vector.<int>> = new <int>[new Vector.<int>(3), new Vector.<int>(3)];
// 2x3 array of these elements
var vals:Vector.<Vector.<int>> = new <int>[
	new <int>[1, 2, 3],
	new <int>[4, 5, 6]
];
// 2x3x4 array of default elements
var vals:Vector.<Vector.<Vector.<int>>> = new <int>[
	new <int>[
		new <int>[ new Vector.<int>(4) ],
		new <int>[ new Vector.<int>(4) ],
		new <int>[ new Vector.<int>(4) ]
	],
	new <int>[
		new <int>[ new Vector.<int>(4) ],
		new <int>[ new Vector.<int>(4) ],
		new <int>[ new Vector.<int>(4) ]
	]
];
// 2x3x4 array of these elements
var vals:Vector.<Vector.<Vector.<int>>> = new <int>[
	new <int>[
		new <int>[ new <int>[1, 2, 3, 4] ],
		new <int>[ new <int>(5, 6, 7, 8) ],
		new <int>[ new <int>(9, 10, 11, 12) ]
	],
	new <int>[
		new <int>[ new <int>(13, 14, 15, 16) ],
		new <int>[ new <int>(17, 18, 19, 20) ],
		new <int>[ new <int>(21, 22, 23, 24) ]
	]
];
 
var val:int = vals[1][2]; // get array element at (1,2)
var val:int = vals[1][2][3]; // get array element at (1,2,3)
 
// (Array version omitted for brevity)

The AS3 version involves a lot of nested objects, each individually allocated and garbage-collected. Indexing into multi-dimensional arrays/vectors involves an index operation for each level of the array. This means there are three lookups for the 2x3x4 array above. In contrast, C#’s multi-dimensional arrays are a single block of memory that happens to know its dimensions. Querying them always involves one lookup, regardless of how many dimensions there are. The address of vals[1,2] in a 2×3 array is the same as a one-dimensional array’s val[1*2 + 1*3] or val[5] . Essentially, expensive lookups are replaced with cheap multiplication and addition.

With resource allocation out of the way, let’s move on to the releasing those resources. Back inpart fiveof this series we talked about destructors, also known as finalizers. Here’s how they look:

class File
{
	public File(string path)
	{
		// ... open the file
	}
 
	// Destructor:
	~File()
	{
		// ... close the file
	}
}

The destructor is called by the garbage collector when it collects instances of the class. They’re a good way to clean up resources like file handles and database connections when an object is destroyed. Explicit cleanup is therefore not required. However, it may be desired since there’s no guarantee about when the garbage collector will get around to calling your destructor. This means it’s usually a good idea to provide an explicit way to clean up the resources.

It’d be easy to add a function with a suggestive name like Dispose() or Destroy() like we do in AS3 and manually call it when we want to clean up. In fact, the System.IDisposable interface is there to standardize just that:

class File : IDisposable
{
	public File(string path)
	{
		// ... open the file
	}
 
	public void Dispose()
	{
		// ... close the file
	}
 
	~File()
	{
		// ... close the file
	}
}

If you adhere to the standard IDisposable interface by implementing it, you get access to a nifty language feature: using blocks. These blocks allow you to declare an IDisposable object scoped to only the block and have its Dispose() automatically called at the end of the block. Here’s how it works:

using (File someFile = new File("/path/to/some/file"))
{
	// ... use the file
}
// ... the File's Dispose() is implicitly called here

In AS3, neither of these language features exists. You must explicitly declare a Dispose() -style function and remember to call it when you’re done:

var someFile:File = new File("/path/to/some/file");
// ... use the file
someFile.Dispose();

The only saving grace is that destructors/finalizers and using / IDisposable are relatively rare as they mostly handle edge cases like file handles and other system resources. Usually, you can simply rely on the garbage collector to do its job after you’ve released all of your references to an object.

That wraps up today’s topic on resource allocation and cleanup. To summarize, here’s a side-by-side of today’s topics in C# and AS3:

////////
// C# //
////////
// Initialize a class or struct instance
Point p = new Point();
 
// Initialize a 1D array
int[] many = new int[200];
 
// Initialize a 1D array of specific values
int[] specific = {1, 2, 3};
 
// Initialize a 2D array
int[,] many2D = new int[2,3];
 
// Initialize a 2D array of specific values
int[,] specific = {{1,2,3}, {4,5,6}};
 
class File : IDisposable
{
	// Explicitly release resources
	public void Dispose()
	{
	}
 
	// Implicitly release resources
	~File()
	{
	}
}
 
// Auto-release resources at end of block
using (File someFile = new File("/path/to/some/file"))
{
}
/////////
// AS3 //
/////////
// Initialize a class or struct instance
var p:Point = new Point();
 
// Initialize a 1D array
var many:Vector.<int> = new Vector.<int>(200);
 
// Initialize a 1D array of specific values
var specific:Vector.<int> = new <int>[1, 2, 3];
 
// Initialize a 2D array
var many2D:Vector.<int> = new <int>(new Vector.<int>(3), Vector.<int>(3));
 
// Initialize a 2D array of specific values
var specific:Vector.<Vector.<int>> = new <int>[new <int>[1,2,3], new <int>[4,5,6]];
 
class File
{
	// Explicitly release resources
	public void dispose()
	{
	}
 
	// Implicitly release resources
	// {impossible in AS3}
 
 
}
 
// Auto-release resources at end of block
// {impossible in AS3}
//
//

We’ll pick up again next week by covering C#’s built-in SQL-style syntax for querying normal objects like arrays. Stay tuned!

Spot a bug? Have a question or suggestion? Post acomment!





About List