TypeLoadException with C# struct using Pointers

43 views Asked by At

Let me start off by saying, this is a hobby project for fun, the stakes are low, and I know that code like this is absolutely not recommended. I will provide context and the relevant code, then at the bottom describe the error.

Constraints: all code in this project must be unmanaged. This means no non-static classes, i.e. structs and other value types only.

I'm creating a small library of unmanaged tools and containers for use in another separate project of mine. I wanted to extract this logic out to keep things clear, and use it as an opportunity to refactor and clean what I had. One of my containers is a linked list implementation. Normally with structs, you can't make a linked list, because a struct cannot have a field of its own type. So I am using pointers.

At the base level I have a set of functions to handle allocating, freeing, copying memory allocations. I'm omitting as much as possible to keep this clean and short. This is the low level allocation of memory.

private static IntPtr Malloc<TUnmanaged>
(
    int length
) where TUnmanaged : unmanaged
{
    ...
}

private static void Free
(
    IntPtr memoryPointer
)
{
    ...
}

To allocate a pointer to a Struct Foo (Foo*):

Foo* pointer = (Foo*)Malloc<Foo>(1);
Free((IntPtr)pointer);

However, I have rules in place designed to prevent passing around raw pointers as much as possible, and ensuring that I never do Free() twice on the same pointer. Each allocation has an owner struct, whose responsibility is to hold the pointer, allow access to the data, and ultimately free it via a Dispose().

public readonly unsafe struct AllocationOwner<TUnmanaged> : IDisposable where TUnmanaged : unmanaged
    {
        public TUnmanaged* Pointer { get; }

        public AllocationOwner
        (
            TUnmanaged* pointer
        )
        {
            Pointer = pointer;
        }

        public TUnmanaged this
        [
            int index
        ]
        {
            get => *(Pointer + index);
            set => *(Pointer + index) = value;
        }

        public AllocationReference<TUnmanaged> ToReference()
        {
            return new AllocationReference<TUnmanaged>(this);
        }

        public void Dispose()
        {
            Allocation.Free(this.Pointer);
        }
    }

AllocationReference is identical, except it is intended simply to be used to get the data, and cannot Free it under any circumstance, i.e. no Dispose().

To be clear, everything up to this point works perfectly, exactly as intended. I have two containers (PointerList, PointerArray), complete with unit tests. PointerArray is essentially a wrapper around the pointer with convenience functions and bounds checking, PointerList is a more traditional list implementation with Add() and dynamic resizing.

The problem arises with my LinkedList and ancillary LinkedNode implementation. I originally wrote this project using raw TUnmanaged* as the backing for all of these containers, and they all worked. When I converted the LinkedNode to use AllocationOwner, that's when the error started. This is linked node:

public unsafe struct LinkedNode<TUnmanaged> : IDisposable where TUnmanaged : unmanaged
    {
        public readonly TUnmanaged Value;
        public AllocationReference<LinkedNode<TUnmanaged>> Next => _next.ToReference();
        private AllocationOwner<LinkedNode<TUnmanaged>> _next;

        public LinkedNode
        (
            TUnmanaged value
        )
        {
            Value = value;
            _next = default;
        }

        //... omitted implementation functions
    }

This compiles fine, but cannot be loaded in runtime:

using Core.Containers;
Console.WriteLine("Hello, World!");

var a = new LinkedNode<float>(1.337f);

The usage above is the most bare bones possible usage. No allocation occurs, it's a simple instantiation of the type, which would result in a LinkedNode object, who has a Value of 1.337f, and who's Next will be a default AllocationOwner object, i.e. Null/IntPtr.Zero. The problem is that this code won't even run. With the following error:

Unhandled exception. System.TypeLoadException: Could not load type 'Core.Containers.LinkedNode`1' from assem bly 'UnmanagedCore, Version=0.0.1.0, Culture=neutral, PublicKeyToken=null'. at Program.$(String[] args)

The runtime simply can't seem to load the type. Even though the following does work:

public unsafe struct LinkedNode<TUnmanaged> : IDisposable where TUnmanaged : unmanaged
    {
        public readonly TUnmanaged Value;
        private LinkedNode<TUnmanaged>* _next;

        public LinkedNode
        (
            TUnmanaged value
        )
        {
            Value = value;
            _next = default;
        }

        //... omitted implementation functions
        }
    }

Any ideas on what is causing this, or how I can better diagnose the TypeLoadException? I get no further information about what went wrong, just that it won't load.

0

There are 0 answers