From AS3 to C#, Part 13: Where Everything Goes
Today we continue the series by wrapping up C#’s class/interface/struct/enum system with a discussion of where to put them all. We’ll focus on package
/namespace
, organizing types into files, and some details of using
/import
.
Table of Contents
- From AS3 to C#, Part 1: Class Basics
- From AS3 to C#, Part 2: Extending Classes and Implementing Interfaces
- From AS3 to C#, Part 3: AS3 Class Parity
- From AS3 to C#, Part 4: Abstract Classes and Functions
- From AS3 to C#, Part 5: Static Classes, Destructors, and Constructor Tricks
- From AS3 to C#, Part 6: Extension Methods and Virtual Functions
- From AS3 to C#, Part 7: Special Functions
- From AS3 to C#, Part 8: More Special Functions
- From AS3 to C#, Part 9: Even More Special Functions
- From AS3 to C#, Part 10: Alternatives to Classes
- From AS3 to C#, Part 11: Generic Classes, Interfaces, Methods, and Delegates
- From AS3 to C#, Part 12: Generics Wrapup and Annotations
- From AS3 to C#, Part 13: Where Everything Goes
- From AS3 to C#, Part 14: Built-in Types and Variables
- From AS3 to C#, Part 15: Loops, Casts, and Operators
- From AS3 to C#, Part 16: Lambdas and Delegates
- From AS3 to C#, Part 17: Conditionals, Exceptions, and Iterators
- From AS3 to C#, Part 18: Resource Allocation and Cleanup
- From AS3 to C#, Part 19: SQL-Style Queries With LINQ
- From AS3 to C#, Part 20: Preprocessor Directives
- From AS3 to C#, Part 21: Unsafe Code
- From AS3 to C#, Part 22: Multi-Threading and Miscellany
- From AS3 to C#, Part 23: Conclusion
In AS3, there are classes, interfaces, and functions but not structures or enums. You can choose to put them in a package or not. That looks like this:
// MyClass.as - not in a package package { class MyClass {} } // MyInterface.as - not in a package package { interface MyInterface {} } // foo.as - not in a package package { function foo(): void {} } // com/jacksondunstan/examples/MyClass.as - in a package package com.jacksondunstan.examples { class MyClass {} } // com/jacksondunstan/examples/MyInterface.as - in a package package com.jacksondunstan.examples { interface MyInterface {} } // com/jacksondunstan/examples/foo.as - in a package package com.jacksondunstan.examples { function foo(): void {} }
To gain access to all the contents of a package, you use import packagename.*
at the start of the package:
package { import com.jacksondunstan.examples.*; public class Example { // Now use something from the package private var myc:MyClass; } }
Or you can gain access to just one class, interface, or function by using import packagename.itemname
:
package { import com.jacksondunstan.examples.MyClass; public class Example { // Now use the imported item from the package private var myc:MyClass; } }
Or you can skip the import
and explicitly qualify your access by using fully-qualified names:
package { public class Example { // Use the fully-qualified item from the package private var myc:com.jacksondunstan.examples.MyClass; } }
If the class, interface, or function you want to use isn’t in a package, there’s no way to explicitly import it. It is simply globally available. So the Example
class can be used by the MyClass
class:
package com.jacksondunstan.examples { class MyClass { private var ex:Example; } }
C# does this a little differently. The namespace
keyword replaces package
. Instead of using package
with no name, you can just omit the namespace
altogether.
// MyClass.cs - not in a namespace class MyClass {} // MyInterface.cs - not in a namespace interface MyInterface {} // MyEnum.cs - not in a namespace enum MyEnum {} // MyStruct.cs - not in a namespace struct MyStruct {} // com/jacksondunstan/examples/MyClass.cs - in a namespace namespace com.jacksondunstan.examples { class MyClass {} } // com/jacksondunstan/examples/MyInterface.cs - in a namespace namespace com.jacksondunstan.examples { interface MyInterface {} } // com/jacksondunstan/examples/MyEnum.cs - in a namespace namespace com.jacksondunstan.examples { enum MyEnum {} } // com/jacksondunstan/examples/MyStruct.cs - in a namespace namespace com.jacksondunstan.examples { struct MyStruct {} }
The paths for each of these files are just suggestions with C#. You can put them anywhere, but the convention is for the paths to match the namespace names like in AS3. For usage, the using
keyword replaces import
and you omit the .*
portion when importing a whole package/namespace:
using com.jacksondunstan.examples; namespace myexamples { public class Example { // Now use something from the namespace private MyClass myc; } }
If you have a static class, you can import just that from a namespace by adding its name to the using
statement:
using com.jacksondunstan.examples.MyStaticClass;
You can also move the using
statements inside your namespace for a subtle difference in behavior:
namespace myexamples { using com.jacksondunstan.examples; public class Example { // Now use something from the namespace private MyClass myc; } }
If the using
is outside the namespace myexamples
then the myexamples
namespace will be searched for MyClass
before com.jacksondunstan.examples
is. It’s the other way around if you put the using
statement inside the namespace examples
.
Another feature of using
is to create aliases. This is useful when you’ve got identically-named classes in multiple namespaces and would rather not use fully-qualified names.
using SystemObject = System.Object; using UnityObject = UnityEngine.Object; class Example { private SystemObject system; private UnityObject unity; }
These have essentially created a new type which is an alias for a type in another namespace.
Like AS3, you can also skip the using
and use fully-qualified names if you’d rather go that route:
namespace myexamples { public class Example { // Now use something from the namespace private com.jacksondunstan.examples.MyClass myc; } }
Unlike AS3’s global accessibility of classes, interfaces, and functions that aren’t in a package, C# makes you explicitly qualify your access using the global::
prefix:
namespace com.jacksondunstan.examples { class MyClass { private var global::Example ex; } }
In AS3, you can only have one public class or interface in a file. The others need to be local to that class only:
public class MyClass { private var helper:HelperClass; } internal class HelperClass { }
C# has no such prohibition. You can have as many public types as you want in a single file:
public class MyClass { private HelperClass helper; } public class HelperClass { }
You can also nest your classes, interfaces, and structs within each other:
public class MyClass { public class HelperClass { } private HelperClass helper; }
To use a nested class from within the class, use it like normal:
HelperClass helper = new HelperClass();
To use it from outside the class, just qualify it with a .
like you’d do with namespaces:
MyClass.HelperClass helper = new MyClass.HelperClass();
The nested classes get visibility into the class their nested in, so they do things like write to private variables:
public class MyClass { private static int health; public class Helper { public void Foo() { health += 2; } } }
Finally, let’s summarize this all with a quick comparison between C# and AS3 for everything discussed today:
//////// // C# // //////// // Type outside of a package public class MyClass {} // Type inside a package namespace mypackage { public class MyClass { // Nest a type public class NestedClass {} } } // Another type in the same file public class HelperClass {} // Get access to the types in a package using mypackage; // Get access to a type in a package - static classes only in C# using mypackage.MyStaticClass; // Create an alias for a type in a package using UnityObject = UnityEngine.Object; // Explicitly use a type in a package mypackage.MyClass myc; // Explicitly use a type outside of a package global::MyClass myc;
///////// // AS3 // ///////// // Type outside of a package package { public class MyClass {} } // Type inside a package package mypackage { public class MyClass { // Nest a type // {impossible in AS3} } } // Another type in the same file: non-public only internal class HelperClass {} // Get access to the types in a package import mypackage.*; // Get access to a type in a package import mypackage.MyClass; // Create an alias for a type in a package // {impossible in AS3} // Explicitly use a type in a package var myc:mypackage.MyClass; // Explicitly use a type outside of a package global::MyClass myc;
This wraps up C#’s object model. Next week we’ll move on to the remainder of the syntax (types, casts, etc.). Stay tuned!
Spot a bug? Have a question or suggestion? Post a comment!
#1 by FMPraxis on November 16th, 2014 ·
Namespaces and packages are not exactly equivalent. Packages in AS3 imply the directory structure of the source whereas namespaces primarily have to do with the organization of the code. AS3 uses the directory structure to support its “object-orientedness” and build in some additional modularity, C# doesn’t particularly care about the directory or even if there is one (you technically can write any C# program in one file, just like C). That said, I don’t suppose there’s anything stopping someone from using C# namespaces like packages, but it will look very strange to all the other C# programmers – and someone will probably tell you you’re doing it wrong.
#2 by jackson on November 16th, 2014 ·
That’s a good point- namespaces need not match the directory structure like packages require in AS3. However, from my experience it’s common for them to match somewhat closely. For example, Microsoft Visual Studio (at least version 2013) will auto-name your namespaces based on the directory the file is in. In the end though, it’s what you say- up to the individual programmer or team of programmers to come up with whatever naming convention they want. C# allows that flexibility.