Compartilhar via


Formas de marshaller personalizadas

Este artigo descreve as diferentes "formas" de marshallers personalizados que podem ser usados com o gerador de código fonte de interoperabilidade do .NET.

Marshallers de valor

Esta seção descreve as formas de marshallers personalizados que podem ser usados pelo gerador de código-fonte de interoperabilidade do .NET para tipos de valor entre código gerenciado e não gerenciado.

Sem estado gerenciado para não gerenciado

Com esse formato, o código gerado invoca ConvertToUnmanaged para transferir um valor ao código nativo ou GetPinnableReference quando aplicável. O código gerado chama Free quando aplicável para permitir que o marshaller libere todos os recursos não gerenciados associados ao tipo gerenciado quando seu tempo de vida terminar.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
static class TMarshaller
{
    public static class ManagedToNative
    {
        /// <summary>
        /// Converts a managed type to an unmanaged representation. May throw exceptions.
        /// </summary>
        public static TNative ConvertToUnmanaged(TManaged managed);

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// </summary>
        public static ref TNativeDereferenced GetPinnableReference(TManaged managed);

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Sem estado gerenciado para não gerenciado com buffer alocado por chamador

Dessa forma, o gerador alocará um buffer do tamanho especificado e o passará para o método ConvertToUnmanaged para fazer o marshaling de um valor para código nativo. O código gerado manipula o tempo de vida desse buffer.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
static class TMarshaller
{
    public static class ManagedToNative
    {
        /// <summary>
        /// The size of the buffer that will be allocated by the generator.
        /// </summary>
        public static int BufferSize { get; }

        /// <summary>
        /// Converts a managed type to an unmanaged representation using a caller-allocated buffer.
        /// </summary>
        public static TNative ConvertToUnmanaged(TManaged managed, Span<byte> callerAllocatedBuffer);

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Sem estado não gerenciado para gerenciado

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
static class TMarshaller
{
    public static class NativeToManaged
    {
        /// <summary>
        /// Converts an unmanaged representation to a managed type. May throw exceptions.
        /// </summary>
        public static TManaged ConvertToManaged(TNative unmanaged);

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Conversão de sem estado não gerenciado para gerenciado com unmarshalling garantido

Essa forma direciona o gerador para emitir o código de unmarshalling de uma maneira que garanta que o processo de unmarshalling ocorra, mesmo que um marshaller anterior lance uma exceção. Isso é útil para tipos que precisam ser limpos ou finalizados, independentemente do sucesso das operações anteriores.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
static class TMarshaller
{
    public static class NativeToManaged
    {
        /// <summary>
        /// Converts an unmanaged representation to a managed type.
        /// Should not throw exceptions.
        /// </summary>
        public static TManaged ConvertToManagedFinally(TNative unmanaged);

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Bidirecional sem estado

Essa forma permite conversões tanto de gerenciado para não gerenciado quanto de não gerenciado para gerenciado, com o marshaller sendo sem estado. O gerador usará o método ConvertToUnmanaged para conversões de gerenciado para não gerenciado e o método ConvertToManaged para conversões de não gerenciado para gerenciado.

[CustomMarshaller(typeof(TManaged<,,,...>), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TManaged<,,,...>), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TManaged<,,,...>), MarshalMode.ElementRef, typeof(Bidirectional))]
static class TMarshaller<T, U, V...>
{
    public static class Bidirectional
    {
        // Include members from each of the following:
        // - One Stateless Managed->Unmanaged Value shape
        // - One Stateless Unmanaged->Managed Value shape
    }
}

Com estado gerenciado para não gerenciado

Essa forma permite o marshalling com estado de gerenciado para não gerenciado. O código gerado usará uma instância exclusiva de marshaller para cada parâmetro, permitindo que o marshaller mantenha o estado durante o processo de marshalling.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
static class TMarshaller
{
    public struct ManagedToNative // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public ManagedToNative();

        /// <summary>
        /// Takes a managed type to be converted to an unmanaged representation in ToUnmanaged or GetPinnableReference.
        /// </summary>
        public void FromManaged(TManaged managed);

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to ToUnmanaged for marshalling.
        /// </summary>
        public ref TIgnored GetPinnableReference();

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, only this method is called for the marshalling step.
        /// May throw exceptions.
        /// </summary>
        public static ref TOther GetPinnableReference(TManaged managed);

        /// <summary>
        /// Converts the managed type to an unmanaged representation.
        /// May throw exceptions.
        /// </summary>
        public TNative ToUnmanaged();

        /// <summary>
        /// Optional.
        /// In managed to unmanaged stubs, this method is called after call to the unmanaged code.
        /// Must not throw exceptions.
        /// </summary>
        public void OnInvoked();

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Com estado gerenciado para não gerenciado com buffer alocado por chamador

Essa forma permite o marshalling com estado de gerenciado para não gerenciado, com o gerador alocando um buffer do tamanho especificado e passando-o para o método FromManaged. O código gerado usará uma instância exclusiva de marshaller para cada parâmetro, permitindo que o marshaller mantenha o estado durante o processo de marshalling.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
static class TMarshaller
{
    public struct ManagedToNative // Can be ref struct
    {
        /// <summary>
        /// The length of the buffer that will be allocated by the generator.
        /// </summary>
        public static int BufferSize { get; }

        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public ManagedToNative();

        /// <summary>
        /// Takes a managed type to be converted to an unmanaged representation in ToUnmanaged or GetPinnableReference.
        /// May throw exceptions.
        /// </summary>
        public void FromManaged(TManaged managed, Span<TBuffer> buffer);

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to ToUnmanaged for marshalling.
        /// </summary>
        public ref TIgnored GetPinnableReference();

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, only this method is called for the marshalling step.
        /// May throw exceptions.
        public static ref TOther GetPinnableReference(TManaged managed);

        /// <summary>
        /// Returns the unmanaged representation of the managed value.
        /// May throw exceptions.
        /// </summary>
        public TNative ToUnmanaged();

        /// <summary>
        /// Optional.
        /// In managed to unmanaged stubs, this method is called after call to the unmanaged code.
        /// Must not throw exceptions.
        /// </summary>
        public void OnInvoked();

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Com estado não gerenciado para gerenciado

Essa forma permite o unmarshalling com estado de não gerenciado para gerenciado. O código gerado usará uma instância exclusiva para cada parâmetro, de modo que a estrutura possa manter o estado na deserialização.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
static class TMarshaller
{
    public struct NativeToManaged // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public NativeToManaged();

        /// <summary>
        /// Takes an unmanaged representation to be converted to a managed type in ToManaged.
        /// Should not throw exceptions.
        /// </summary>
        public void FromUnmanaged(TNative unmanaged);

        /// <summary>
        /// Returns the managed value representation of the unmanaged value.
        /// May throw exceptions.
        /// </summary>
        public TManaged ToManaged();

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the unmarshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Conversão de com estado não gerenciado para gerenciado com unmarshalling garantido

Essa forma permite o unmarshalling com estado de não gerenciado para gerenciado, com o gerador garantindo que o método ToManagedFinally seja chamado mesmo se um marshaller anterior gerar uma exceção. Isso é útil para tipos que precisam ser limpos ou finalizados, independentemente do sucesso das operações anteriores.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
static class TMarshaller
{
    public struct NativeToManaged // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public NativeToManaged();

        /// <summary>
        /// Takes an unmanaged representation to be converted to a managed type in ToManagedFinally.
        /// Should not throw exceptions.
        /// </summary>
        public void FromUnmanaged(TNative unmanaged);

        /// <summary>
        /// Returns the managed value representation of the unmanaged value.
        /// Should not throw exceptions.
        /// </summary>
        public TManaged ToManagedFinally();

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the unmarshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Bidirecional com estado

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))]
static class TMarshaller
{
    public struct Bidirectional // Can be ref struct
    {
        // Include members from each of the following:
        // - One Stateful Managed->Unmanaged Value shape
        // - One Stateful Unmanaged->Managed Value shape
    }
}

Marshallers de coleção

Esta seção descreve as formas de marshallers de coleção personalizados que o gerador de código-fonte de interoperabilidade do .NET dá suporte para fazer marshalling de coleções entre código gerenciado e não gerenciado. Os marshallers de coleções contíguas são usados para coleções representadas como um bloco contíguo de memória na representação não gerenciada, como arrays ou listas.

Quando o tipo de coleção que está sendo agrupado é genérico sobre seu tipo de elemento (por exemplo, List<T>), o tipo de marshaller personalizado normalmente terá dois parâmetros genéricos: um para o tipo de elemento gerenciado e outro para o tipo de elemento não gerenciado. Nos casos em que a coleção não é genérica sobre seu tipo de elemento (por exemplo, StringCollection), o marshaller pode ter apenas um único parâmetro genérico para o tipo de elemento não gerenciado. Os exemplos de configuração do marshaller demonstram um marshaller para uma coleção TCollection não genérica com elementos do tipo TManagedElement. Como os elementos podem ser controlados usando diferentes marshallers de valor, ele deve ser genérico em relação ao tipo de elemento não gerenciado, TUnmanagedElement. A representação nativa da coleção é chamada TNative (normalmente é *TUnmanagedElement).

Sem estado gerenciado para não gerenciado

Com esse formato, o gerador utiliza métodos estáticos para realizar a transferência de uma coleção gerenciada para a memória não gerenciada. O marshaller deve fornecer métodos para alocar o contêiner não gerenciado, acessar elementos gerenciados e não gerenciados e, opcionalmente, liberar recursos não gerenciados.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TCollection), MarshalMode.ElementIn, typeof(ManagedToNative))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class ManagedToNative
    {
        /// <summary>
        /// Gets the uninitialized unmanaged container for the elements and assigns the number of elements to numElements.
        /// The return value is later passed to GetUnmanagedValuesDestination.
        /// </summary>
        public static TNative AllocateContainerForUnmanagedElements(TCollection managed, out int numElements);

        /// <summary>
        /// Gets a span of managed elements in the collection.
        /// The elements in this span are marshalled by the element marshaller and put into the unmanaged container.
        /// </summary>
        public static ReadOnlySpan<TManagedElement> GetManagedValuesSource(TCollection managed);

        /// <summary>
        /// Gets a span of unmanaged elements from the TNative container.
        /// The elements in this span are filled with the marshalled values from the managed collection.
        /// </summary>
        public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TNative unmanaged, int numElements);

        /// <summary>
        /// Optional. Returns a pinnable reference to the unmanaged representations of the elements.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// </summary>
        public static ref TOther GetPinnableReference(TManaged managed);

        /// <summary>
        /// Optional. Frees unmanaged resources.
        /// Should not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Sem estado gerenciado para não gerenciado com buffer alocado por chamador

O gerador aloca um buffer e o passa para o marshaller para uso durante o marshalling.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TCollection), MarshalMode.ElementIn, typeof(ManagedToNative))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class ManagedToNative
    {
        /// <summary>
        /// The length of the buffer that will be allocated by the generated code and passed to AllocateContainerForUnmanagedElements.
        /// </summary>
        public static int BufferSize { get; }

        /// <summary>
        /// Creates the unmanaged container using a caller-allocated buffer, and assigns the number of elements in the collection to numElements.
        /// The return value is later passed to GetUnmanagedValuesDestination.
        /// </summary>
        public static TNative AllocateContainerForUnmanagedElements(TCollection managed, Span<TBuffer> buffer, out int numElements);

        /// <summary>
        /// Gets a span of managed elements in the collection.
        /// The elements in this span are marshalled by the element marshaller and put into the unmanaged container.
        /// </summary>
        public static ReadOnlySpan<TManagedElement> GetManagedValuesSource(TCollection managed);

        /// <summary>
        /// Gets a span of unmanaged elements from the TNative container.
        /// The elements in this span are filled with the marshalled values from the managed collection.
        /// </summary>
        public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TNative unmanaged, int numElements);

        /// <summary>
        /// Optional. Returns a pinnable reference to the unmanaged representations of the elements.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// </summary>
        public static ref TOther GetPinnableReference(TManaged managed);

        /// <summary>
        /// Optional. Frees unmanaged resources.
        /// Should not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Sem estado não gerenciado para gerenciado

Esta forma realiza a conversão de memória não gerenciada para uma coleção gerenciada.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TCollection), MarshalMode.ElementOut, typeof(NativeToManaged))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class NativeToManaged
    {
        /// <summary>
        /// Allocates a new collection for unmarshalling.
        /// May throw exceptions.
        /// </summary>
        public static TCollection AllocateContainerForManagedElements(TNative unmanaged, int numElements);

        /// <summary>
        /// Gets the destination span that unmarshalled managed elements will be written to.
        /// May throw exceptions.
        /// </summary>
        public static Span<TManagedElement> GetManagedValuesDestination(TCollection managed);

        /// <summary>
        /// Gets the source span of unmanaged elements to unmarshal from.
        /// May throw exceptions.
        /// </summary>
        public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TNative unmanaged, int numElements);

        /// <summary>
        /// Optional. Frees unmanaged resources.
        /// Should not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Conversão de sem estado não gerenciado para gerenciado com unmarshalling garantido

O gerador garante que o unmarshalling ocorra mesmo se um marshaller anterior gerar uma exceção.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class NativeToManaged
    {
        /// <summary>
        /// Allocates the managed container for the elements.
        /// Should not throw exceptions other than OutOfMemoryException.
        /// </summary>
        public static TCollection AllocateContainerForManagedElementsFinally(TNative unmanaged, int numElements);

        /// <summary>
        /// Gets the destination span that unmarshalled managed elements will be written to.
        /// May throw exceptions.
        /// </summary>
        public static Span<TManagedElement> GetManagedValuesDestination(TCollection managed);

        /// <summary>
        /// Gets the source span of unmanaged elements to unmarshal from.
        /// May throw exceptions.
        /// </summary>
        public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TNative unmanaged, int numElements);

        /// <summary>
        /// Optional. Frees unmanaged resources.
        /// Should not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Bidirecional sem estado

Essa forma oferece suporte às conversões de código gerenciado para não gerenciado e de não gerenciado para gerenciado.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TCollection), MarshalMode.ElementRef, typeof(Bidirectional))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class Bidirectional
    {
        // Include members from each of the following:
        // - One Stateless Managed->Unmanaged Linear Collection shape
        // - One Stateless Unmanaged->Managed Linear Collection shape
    }
}

Com estado gerenciado para não gerenciado

Esta forma permite que o marshaller mantenha o estado durante todo o processo de marshalling.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct ManagedToNative // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public ManagedToNative(); // Optional, can throw exceptions.

        /// <summary>
        /// Takes a managed collection to be converted to an unmanaged representation in ToUnmanaged or GetPinnableReference.
        /// </summary>
        public void FromManaged(TCollection collection); // Can throw exceptions.

        /// <summary>
        /// Gets the source span of managed elements to marshal.
        /// May throw exceptions.
        /// </summary>
        public ReadOnlySpan<TManagedElement> GetManagedValuesSource(); // Can throw exceptions.

        /// <summary>
        /// Gets the destination span of unmanaged elements to marshal to.
        /// May throw exceptions.
        /// </summary>
        public Span<TUnmanagedElement> GetUnmanagedValuesDestination(); // Can throw exceptions.

        /// <summary>
        /// Optional.
        /// Gets a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to ToUnmanaged for marshalling.
        /// May throw exceptions.
        /// </summary>
        public ref TOther GetPinnableReference(); // Optional. Can throw exceptions.

        /// <summary>
        /// Optional.
        /// Gets a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to the instance version and ToUnmanaged for marshalling.
        /// May throw exceptions.
        /// </summary>
        public static ref TOther GetPinnableReference(TCollection collection); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.

        /// <summary>
        /// Converts the managed collection to an unmanaged representation.
        /// May throw exceptions.
        /// </summary>
        public TNative ToUnmanaged(); // Can throw exceptions.

        /// <summary>
        /// Optional.
        /// In managed to unmanaged stubs, this method is called after call to the unmanaged code.
        /// Must not throw exceptions.
        /// </summary>
        public void OnInvoked(); // Optional. Should not throw exceptions.
    }
}

Com estado gerenciado para não gerenciado com buffer alocado por chamador

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct ManagedToNative // Can be ref struct
    {
        /// <summary>
        /// The length of the buffer that will be allocated by the generated code and passed to FromManaged.
        /// </summary>
        public static int BufferSize { get; }

        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public ManagedToNative();

        /// <summary>
        /// Takes a managed collection to be converted to an unmanaged representation in ToUnmanaged or GetPinnableReference.
        /// The caller-allocated buffer is passed to this method.
        /// </summary>
        public void FromManaged(TCollection collection, Span<TBuffer> buffer);

        /// <summary>
        /// Gets the source span of managed elements to marshal.
        /// May throw exceptions.
        /// </summary>
        public ReadOnlySpan<TManagedElement> GetManagedValuesSource();

        /// <summary>
        /// Gets the destination span of unmanaged elements to marshal to.
        /// May throw exceptions.
        /// </summary>
        public Span<TUnmanagedElement> GetUnmanagedValuesDestination();

        /// <summary>
        /// Optional.
        /// Gets a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to ToUnmanaged for marshalling.
        /// May throw exceptions.
        /// </summary>
        public ref TOther GetPinnableReference();

        /// <summary>
        /// Optional.
        /// Gets a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to the instance GetPinnableReference and ToUnmanaged for marshalling.
        /// May throw exceptions.
        /// </summary>
        public static ref TOther GetPinnableReference(TCollection collection);

        /// <summary>
        /// Converts the managed collection to an unmanaged representation.
        /// May throw exceptions.
        /// </summary>
        public TNative ToUnmanaged();

        /// <summary>
        /// Optional.
        /// In managed to unmanaged stubs, this method is called after call to the unmanaged code.
        /// Must not throw exceptions.
        /// </summary>
        public void OnInvoked();
    }
}

Com estado não gerenciado para gerenciado

Esta forma permite que o marshaller mantenha o estado durante todo o processo de unmarshalling.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct NativeToManaged // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public NativeToManaged();

        /// <summary>
        /// Takes an unmanaged collection to be converted to a managed representation in ToManaged.
        /// Should not throw exceptions.
        /// </summary>
        public void FromUnmanaged(TNative value);

        /// <summary>
        /// Gets the source span of unmanaged elements to unmarshal from.
        /// May throw exceptions.
        /// </summary>
        public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements);

        /// <summary>
        /// Gets the destination span that unmarshalled managed elements will be written to.
        /// May throw exceptions.
        /// </summary>
        public Span<TManagedElement> GetManagedValuesDestination(int numElements);

        /// <summary>
        /// Returns the managed value representation of the unmanaged value.
        /// May throw exceptions.
        /// </summary>
        public TCollection ToManaged();

        /// <summary>
        /// Optional. Should not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Conversão de com estado não gerenciado para gerenciado com unmarshalling garantido

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct NativeToManaged // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public NativeToManaged(); // Optional, can throw exceptions.

        /// <summary>
        /// Takes an unmanaged collection to be converted to a managed representation in ToManagedFinally.
        /// Should not throw exceptions.
        /// </summary>
        public void FromUnmanaged(TNative value);

        /// <summary>
        /// Gets the source span of unmanaged elements to unmarshal from.
        /// May throw exceptions.
        /// </summary>
        public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements);

        /// <summary>
        /// Gets the destination span that unmarshalled managed elements will be written to.
        /// May throw exceptions.
        /// </summary>
        public Span<TManagedElement> GetManagedValuesDestination(int numElements);

        /// <summary>
        /// Returns the managed value representation of the unmanaged value.
        /// Should not throw exceptions.
        /// </summary>
        public TCollection ToManagedFinally();

        /// <summary>
        /// Optional. Should not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Bidirecional com estado

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct Bidirectional // Can be ref struct
    {
        // Include members from each of the following:
        // - One Stateful Managed->Unmanaged Contiguous Collection shape
        // - One Stateful Unmanaged->Managed Contiguous Collection shape
    }
}