Generic Union Type with Explicit Layout

G

Guest

I am desperately trying to build a generic union type with explicit field
layout. First, I will show you my attempt without generics. This is something
like a tagged union in functional languages like F#. Thus let us first write
an example using F#, which is rather short (assuming polymorphic types 'T
stack and 'T queue already defined)
---
type 'T ProcessingOrder = Stack of 'T stack | Queue of 'T queue;;
---
This defines the generic type ProcessingOrder to be either a stack or a
queue, with explicit tags Stack and Queue. 'T is the type parameter. As I
have found out using IL DASM, F# circumvents my problem by using auto layout
and thus does not create a real union type, wasting space.
Let me define the same type using C#:
---
[StructLayout(LayoutKind.Explicit)]
struct ProcessingOrder<T>
{
[FieldOffset(0)] public readonly object _object;
[FieldOffset(0)] public Stack<T> _stack;
[FieldOffset(0)] public Queue<T> _queue;
}
---
Note that I use implicit tagging, as classes in .NET are already tagged. One
may explore the actual type of the union by typeof(_object). Propably not the
fastest way.
Unfortunately, this does not work. Compiles without error messages but at
execution a TypeLoadException is thrown, which tells me that explicit layout
is not allowed for generic types.
Au contraire, a specialized implementation works fine:
---
[StructLayout(LayoutKind.Explicit)]
struct ProcessingOrderInt
{
[FieldOffset(0)] public readonly object _object;
[FieldOffset(0)] public Stack<int> _stack;
[FieldOffset(0)] public Queue<int> _queue;
}
---
Has anyone an idea why explicit layout is forbidden for generic types? Has
anyone an idea how to circumvent this without accouting space for every
choice?

TIA,
Hagen
 
M

Mattias Sjögren

Note that I use implicit tagging, as classes in .NET are already tagged. One
may explore the actual type of the union by typeof(_object). Propably not the
fastest way.

I don't see why you need the _stack and _queue fields at all if you
still are going to work with the Object field _object. Can't you just
do something like

class ProcessingOrder<T>
{
private object _object;

public Stack<T> Stack { get { return (Stack<T>)_object; } }
public Queue<T> Queue { get { return (Queue<T>)_object; } }
public bool IsQueue { get { return _object is Queue<T>; } }
}


Mattias
 
G

Guest

Thanks for pointing that out, Mattias! Very helpful! Class casting and
properties already provide everything necessary for a tagged union type. What
I want is maximum type safety. By making _object private and using properties
I get what I intended.

---
class ProcessingOrder<T>
{
private object _object;

public Stack<T> Stack
{
set { _object = value; }
get { return (Stack<T>) _object; }
}

public Queue<T> Queue
{
set { _object = value; }
get { return (Queue<T>) _object; }
}

public object Object { get { return _object; } }

public ProcessingOrder (Stack<T> toStack) { Stack = toStack; }
public ProcessingOrder(Queue<T> toQueue) { Queue = toQueue; }
}
 
G

Guest

There is an error in my previous solution. You need to assign _object
explcitely in the constructors. Thus my solution is:

---
class ProcessingOrder<T>
{
private object _object;

public Stack<T> Stack
{
set { _object = value; }
get { return (Stack<T>) _object; }
}

public Queue<T> Queue
{
set { _object = value; }
get { return (Queue<T>) _object; }
}

public object Object { get { return _object; } }

public ProcessingOrder (Stack<T> toStack) { _object = toStack; }
public ProcessingOrder(Queue<T> toQueue) { _object = toQueue; }
}
---
 
L

Lloyd Dupont

BTW, you can't make union of managed/reference type.

thus the following is authorized:
[StructLayout(LayoutKind.Explicit)]
struct ProcessingOrder<T>
{
[FieldOffset(0)] public int AnInt;
[FieldOffset(0)] public double ADouble;
[FieldOffset(0)] public DateTime ATime;
}

while this is not
[StructLayout(LayoutKind.Explicit)]
struct ProcessingOrder<T>
{
[FieldOffset(0)] public object AnObject;
[FieldOffset(0)] public Control AControl;
[FieldOffset(0)] public String AString;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top