IronPython: System.Reflection.Assembly object

IronPython offers a little bit more love to the Assembly object instance: we can directly access the assembly's top-level members (namespace, public type) via the dot operation. System.Reflection provides many ways to let you hold the assembly object, such as Assembly.Load method, Type.Assembly property, ...

For example, given assembly1.dll compiled from the C# file below, dir(a1) in the python below shows the class name "C2" and the top level namespace "NS1" in its' dictionary.

 // assembly1.cs
namespace NS1 {
    namespace NS2 {
        public class C1 { }
public class C2 {
    public class C3 { }
class C4 { }

 # part 1 from file/
import System
a1 = System.Reflection.Assembly.Load("assembly1")

print dir(a1)  # ['C2', ..., 'NS1', ...]
print a1.NS1   # <module 'NS1' (CLS module from assembly1, Version=, Culture=neutral, PublicKeyToken=null)>
print a1.C2    # <type 'C2'>

# access non-public type
print a1.C4    # AttributeError: 'Assembly' object has no attribute 'C4'

# access the down-level namespace, type with the dot operations
print a1.NS1.NS2.C1         # <type 'C1'>
# access the nested type
print a1.C2.C3              # <type 'C3'>

Normally I would use clr.AddReference to bring in the CLR assembly. The python code below gives the similar output.

 # part 1 of file/
import clr
import NS1 
import C2

print NS1        # <module 'NS1' (CLS module from assembly1, Version=, Culture=neutral, PublicKeyToken=null)>
print C2         # <type 'C2'>
print NS1.NS2    # <module 'NS2' (CLS module from assembly1, Version=, Culture=neutral, PublicKeyToken=null)>
print NS1.NS2.C1 # <type 'C1'>
print C2.C3      # <type 'C3'>

However the first approach may help keep the global (and local) dictionary less "polluted" and sometimes can be used to avoid the namespace/type name collision. For example, suppose we need to load in another assembly built from the following C# code, and we use the clr.AddReference approach.

 // assembly2.cs
namespace NS1 { 
    public class NS2 { } 
 # part 2 of file/
print NS1.NS2    # <type 'NS2'>
print NS1.NS2.C1 # AttributeError: 'type' object has no attribute 'C1'

NS2 is a type now (no longer the namespace (or python module) NS1.NS2 from "assembly1"), and we lost the access to the type NS1.NS2.C1. If we use the assembly object approach, we can hold both the namespace NS1.NS2 from assembly1 and the type NS1.NS2 from assembly2; basically we can think the assembly variable name adds one more layer to prevent the name collision. Or in the other direction of thinking, what clr.AddReference does for you (related to implicit name merge/override) may not be what exactly you want.

 # part 2 from file/
a2 = System.Reflection.Assembly.Load("assembly2")
print a2.NS1     # <module 'NS1' (CLS module from assembly2, Version=, Culture=neutral, PublicKeyToken=null)>
print a2.NS1.NS2 # <type 'NS2'>
print a1.NS1     # <module 'NS1' (CLS module from assembly1, Version=, Culture=neutral, PublicKeyToken=null)>
print a1.NS1.NS2 # <module 'NS2' (CLS module from assembly1, Version=, Culture=neutral, PublicKeyToken=null)>

Note that such name collision should rarely happen for well-designed framework assemblies. Also IronPython merge types/down-level namespaces under the same namespaces, even if they are from different assemblies. One simple example is System.Int32 from mscorlib.dll and System.Uri from System.dll, both peacefully under the "System" module.

 import System
print System            
# <module 'System' (CLS module, 2 assemblies loaded)>
print System.__file__   
# ['mscorlib, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089', 
#  'System, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089']
import clr
print clr.GetClrType(System.Int32).Assembly  
# <Assembly mscorlib, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089>
print clr.GetClrType(System.Uri).Assembly    
# <Assembly System, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089>