Limitations

C# and ActionScript are quite similar in most ways, which made CS2AS fairly easy to write. However, there are a few key gotchas that you need to be aware of.

Most limitations easy to work around by making minor adjustments to your C# code, however some limitations can be a bit annoying. For more guidance on how to work through converting your code to work with CS2AS, see the Conversion Guide.

Flat-out not supported: goto, yield, unsafe code, anonymous types, operator overloads, ref, out, explicit or implicit cast operators, multi-threading

ActionScript does not support any of these, and CS2AS cannot fix them for you. You must not use any of these constructs in your C# code.

ActionScript does not support overloaded methods.

CS2AS can resolve some of your overloads itself by using default parameters. For example:
int Overloaded()
{
	return Overloaded("DefaultValue");
}

int Overloaded(string parameter)
{
	...
}
CS2AS will detect this situation and generate a single method:
function Overloaded(parameter:String = "DefaultValue"):int
{
	...
}
This only works if your overload does nothing other than call another overload with primitive types. If you do anything more complicated, you will need to remove your overloads by renaming some of them.

Base Class Library Overloads

In the cases where a base class library overloads methods, you must work around this by separating out which overloads you intend to call from your code.

For example, Linq provides two implementations of the First method: One that takes no parameters and one that takes a conditional function.

Luckily extension methods make this easy to solve. We provide an alias for the second overload called FirstWhere:
	#if !CS2AS
	public static T FirstWhere<T>(this IEnumerable<T> e, Func<T, bool> where) { return e.First(where); }
	#endif
Now, we can update our code that uses this overload of First to call FirstWhere instead.

ActionScript does not support structs

ActionScript does not allow you to define your own value types, only reference types. Any structures in you code will be a reference type in ActionScript, and this may cause bugs if your code assumes the structure is being copied by value.

To fix this, change your structs to classes or make the structs immutable. (Mutable structs are a bad idea in C# anyway.)

ActionScript does not have nullable types

For nullable types, CS2AS will replace the type with a class prefixed by Nullable_. For example, int? becomes Nullable_int. They can then be treated as an immutable number type, with .Value and .HasValue properties.

However, this still does not account for all of C#'s syntax. But it is possible to write C# code that works both in C# and ActionScript by following these three rules:
  • Recall that int? is just an alias for Nullable<int>. So, instead of assigning directly to your nullable variable, use new Nullable<>.
For example, instead of writing: int? variable = 4; write int? variable = new Nullable<int>(4)
  • Similar to the above, to set a variable to null use int? variable = new Nullable<int>()
  • Always check the .HasValue property instead of checking for equality to null. These statements are equivilant in C#, but for CS2AS you must use the latter:
if (variable != null) ... //Don't do this
if (variable.HasValue) ... //Do this


In ActionScript land, your Nullable_int types should never be null themselves. Instead, they should have a false HasValue when they represent null. If you see a null when debugging, it means you didn't follow one of these three rules above.

"Using" statements must reference a local variable

CS2AS transforms your "using" statements to a try/finally block. Since the code in the using condition gets called beforehand in C# and afterwards in ActionScript, you can't put code with side-effects into the using condition. For example:

Don't do:
	using (SomeMethod()) { ... }

Instead, do:
	var someObject = SomeMethod();
	using (someObject) { ... }

This will get translated to:

	var someObject:* = SomeMethod();
	try { ... }
	finally { someObject.Dispose(); }

Lists, Stacks and Queues

CS2AS uses the Array type directly to represent the generic types List<>, Stack<> and Queue<>. Most of the time these will just work as you expect as long as CS2AS can identify your type. In some cirumstances, CS2AS can't identify the type of your variable and won't be able to convert it property.

For example, don't do this:
	SomeMethodThatReturnsAList().Add(4);

Instead, do this:
	List<int> list = SomeMethodThatReturnsAList();
	list.Add(4);

That will translate to this ActionScript code:
	var list:Array = SomeMethodThatReturnsAList();
	list.push(4);

Finally, Arrays cannot accept a collection as a default value in ActionScript.

For example, don't do this:
	var someCollection = new int[] { 4, 5, 6 };
	var list = new List<int>(someCollection);

Instead, you must add them individually:
	var list = new List<int>();
	foreach(var o in someCollection)
		list.Add(o);

HashSets

The Dictionary<> and HashSet<> generic types are more complicated to represent in ActionScript. They cannot be represented as Arrays in since Arrays in ActionScript cannot use non-primitive types as keys. Instead, CS2AS defines its own class to represent HashSets named HashSet.

HashSet defines the Add, Remove, and Contains methods that you would expect. The only complication comes when enumerating on a HashSet. Since ActionScript does not allow your own types to define themselves as being enumerable, we must define a method that returns the values in an Array to enumerate on called Values.

For example, don't do this:
	var someHashSet = new HashSet<int>();
	foreach(var val in someHashSet) { ... }

Instead, do this:
	foreach(var val in someHashSet.Values()) { ... }

We define a simple extension method so this also works in C#:
	#if !CS2AS
	public static IEnumerable<T> Values<T>(this HashSet<T> hash) { return hash; }
	#endif

Dictionaries

CS2AS defines its own class to represent dictionaries (CSDictionary) for the same reason as HashSets above. Since ActionScript does not allow user-defined types to overload the [] (indexing) operator, you cannot index on dictionaries.

For example, given a dictionary (var dict = new Dictionary<int, int>()), don't do this:
	var atIndexFour = dict[4];

Instead, do this:
	var atIndexFour = dict.GetValue(4);

We simply define an extension method so that this works in C#:

	#if !CS2AS
	public static V GetValue<K,V>(this Dictionary<K,V> dict, K key) { return dict[key]; }
	#endif

CSDictionary provides a Keys and Values property, so those can be enumerated on just as you normally would in C#:
	foreach(var val in dict.Values) { ... }

However, ActionScript does not allow the core type to be enumerable itself. If you need both the key and the value in your enumeration, you have two options. You can enumerate on the Keys and call GetValue to access the value:
	foreach(var key in dict.Keys) { dict.GetValue(key)... }

Or, CSDictionary provides a KeyValues() method that gets an array of KeyValuePairs so you can do this:
	foreach(var kv in dict.KeyValues()) { kv.Key... kv.Value... }

To make this work in C#, we simply add it as an extension method:
	#if !CS2AS
	public static IEnumerable<KeyValuePair<K,V>> KeyValues<K,V>(this Dictionary<K,V> dict) { return dict; }
	#endif

Note: CS2AS will try to add the .KeyValues() call itself when it can identify that you are enumerating on a dictionary. However, it can only tell if you're foreaching directly on the dictionary that CS2AS knows the type of. If you're using Linq or CS2AS can't identify your foreach type, CS2AS won't know to add the .KeyValues() and you will have to add it yourself. To be safe, you can just always add it yourself.

Initializing fields of a class to non-primitive types

C# allows you to initalize objects as part of a class. ActionScript does not allow this.

For example, don't do:
	class SomeClass
	{
		public SomeObject obj = new SomeObject(); 
	}

Instead, move it to the constructor:
	class SomeClass
	{
		public SomeObject obj;
		public SomeClass()
		{
			obj = new SomeObject();
		}
	}

The "Internal" keyword works differently in ActionScript

ActionScript uses internal to represent visibility within a package whereas C# uses it to represent visibility within an assembly. You may be required to promote some fields to public if they need to be visible across packages.

Unreachable code

C# is much smarter about detecting code flows than ActionScript.

For example, this is valid C#

	public int SomeMethod()
	{
		switch (...)
		{
			case A: return 4;
			case B: return 8;
			default: throw new Exception();
		}
	}

The equivilant ActionScript code would warn that the method does not return a value since it cannot trace the codepaths. Similar errors can occur with loops.

To work around this, just add this to the end of the method:
	#if CS2AS
	throw new Error("This should never be reached");
	#endif

Overriding ToString in child classes

ActionScript has some wonky rules about when to use the keyword "override" when overriding methods. Since CS2AS can't easily determine whether or not to include it, it has to guess sometimes.

You can easily work around any problems here by simply making sure you always override ToString in a parent class. If a child class overrides ToString but the class it directly inherits from does not, your resulting ActionScript code will not compile.

Namespace differences

In C#, a class defined within a namespace One.Two.Three can access a class defined in One.Two without qualifying its namespace.

In ActionScript, classes cannot. To work around this issue, simply include "using One.Two;" at the top of your file.

ActionScript closures don't close over the "this" object

In C#, code such as the following is valid:

public class SomeClass
{
	public int Field;
	
	public void Method()
	{
		Action a = () => Console.WriteLine(this.Field);
	}
}

In ActionScript, the word "this" changes its meaning within a lamda to refer to the lambda, not the containing class. To work around this, simply omit the "this."

public class SomeClass
{
	public int Field;
	
	public void Method()
	{
		Action a = () => Console.WriteLine(Field);
	}
}

If you absolutely must access the "this" object, don't do this:

public class SomeClass
{
	public void MethodWithParam(SomeClass c) { ... }
	
	public void Method()
	{
		Action a = () => MethodWithParam(this);
	}
}

Instead, you will need to refer to the "this" parameter as a local variable to close over:

public class SomeClass
{
	public void MethodWithParam(SomeClass c) { ... }
	
	public void Method()
	{
		var ths = this;
		Action a = () => MethodWithParam(ths);
	}
}

Last edited Mar 2, 2010 at 8:55 PM by fickerra, version 8

Comments

No comments yet.