Based on the comments, I have managed to get this working using a temporary DLL assembly. It's ugly and I'm sure somebody with a better understanding of what goes on "under the hood", can set me straight and improve the answer. Here it is:
$TypeDef1= @'
namespace Mynamespace
{
public class MyType1
{
public string a { get; set; }
public string b { get; set; }
}
}
'@
$type1 = Add-Type $TypeDef1 -PassThru -OutputAssembly "c:\temp\my.dll"
$obj1 = New-Object Mynamespace.MyType1
$obj1.a = 'my a'
$obj1.b = 'my b'
$TypeDef2 = @'
namespace Mynamespace
{
public class MyType2
{
public string c { get; set; }
public Mynamespace.MyType1 subObj { get; set; }
}
}
'@
Add-Type -TypeDefinition $TypeDef2 -ReferencedAssemblies "c:\temp\my.dll"
$obj2 = New-Object Mynamespace.MyType2
$obj2.subObj = $obj1
Basically, result of the first compilation (add-type) is saved in a DLL and this DLL is passed as a referenced assembly to the second add type statement.
I understand that there is already a temp DLL created by add-type statement and can be seen in $type1.Module, but I could not find a way to reference that in the second type-add command.
EDIT:
While trying to figure out how to make this less "ugly", I have come across other people trying to accomplish similar task in C# natively.
In C#, how do you reference types from one in-memory assembly inside another?
C# - Referencing a type in a dynamically generated assembly
The second link points out a method which may be just a little bit more .NETish.
By default PowerShell Add-Type command executes .NET compiler with GenerateInMemory option set to $true.
This compiles code and loads resulting Types into memory, not leaving actual copy of the compiled assembly. A copy of the assembly is requited to compile additional Types which reference the original one.
One way to get around this is to write our own New-Type function. This is a simplified version of the Add-Type cmdlet, which executes the compiler with GenerateInMemory = $false and returns reference to the compiled assembly.
This reference can then be used to compile subsequent Types.
A temporary file is still generated on disk, but at least the process and location are obfuscated by the compiler.
Here is the code:
function New-Type {
param([string]$TypeDefinition,[string[]]$ReferencedAssemblies)
$CodeProvider = New-Object Microsoft.CSharp.CSharpCodeProvider
# Location for System.Management.Automation DLL
$dllName = [PsObject].Assembly.Location
$Parameters = New-Object System.CodeDom.Compiler.CompilerParameters
$RefAssemblies = @("System.dll", $dllName)
$Parameters.ReferencedAssemblies.AddRange($RefAssemblies)
if($ReferencedAssemblies) {
$Parameters.ReferencedAssemblies.AddRange($ReferencedAssemblies)
}
$Parameters.IncludeDebugInformation = $true
$Parameters.GenerateInMemory = $false # Do not compile in memory (generates a temp DLL file)
$Results = $CodeProvider.CompileAssemblyFromSource($Parameters, $TypeDefinition) #compile
if($Results.Errors.Count -gt 0) {
$Results.Errors | % { Write-Error ("{0}:`t{1}" -f $_.Line,$_.ErrorText) }
}
return $Results.CompiledAssembly # return info for the assembly
}
$TypeDef1= @'
public class MyType1
{
public string a { get; set; }
public string b { get; set; }
}
'@
$Asembly1 = New-Type $TypeDef1
$obj1 = New-Object MyType1
$obj1.a = 'my a'
$obj1.b = 'my b'
$TypeDef2 = @'
public class MyType2
{
public string c { get; set; }
public MyType1 subObj { get; set; }
}
'@
$Asembly2 = New-Type -TypeDefinition $TypeDef2 -ReferencedAssemblies $Asembly1.Location
$obj2 = New-Object MyType2
$obj2.subObj = $obj1