diff --git a/Generator.cs b/Generator.cs
index 1c4bfcd..a691a39 100644
--- a/Generator.cs
+++ b/Generator.cs
@@ -8,19 +8,6 @@
// fitness for a particular purpose. Attribution is appreciated, but not
// required.
-// About this version
-//
-// v1.0.0 of this project aimed to provide dynamically instanciated Delegate
-// objects that implemented one of the provided interfaces, but relied on
-// classes from System.Reflection.Emit, which is incompatible with some .NET
-// platforms (those using AOT compilation).
-//
-// This version instead aims to create classes that implement the provided
-// interfaces at compile-time, using an incremental source generator. This
-// version is a work-in-progress that may have bugs and is not feature
-// complete. Importantly, the generated class objects are not instances of the
-// Delegate or MulticastDelegate types.
-
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
@@ -30,8 +17,49 @@
namespace NativeGenericDelegatesGenerator
{
[Generator]
- public class Generator : IIncrementalGenerator
+ internal class Generator : IIncrementalGenerator
{
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The translation phases for generating the source for native generic delegates are:
+ ///
+ ///
+ /// -
+ /// From the syntax tree, select any nodes that begin with the text
+ /// "INativeAction" or "INativeFunc".
+ ///
-
+ /// Filter the selected nodes using the to validate that the method is a native generic
+ /// delegate interface method call with no open generic type parameters. The validated symbol is then paired with a in a . Invalid symbols are filtered out of this
+ /// translation.
+ ///
-
+ /// Custom marshaling behavior is parsed from the user-supplied arguments (e.g., what the user passed for the
+ /// marshalParamsAs argument). A is created which may
+ /// contain a that will be reported in a later translation phase. If this object does contain
+ /// diagnostic info, then no further translations are performed on this object except to report the diagnostic.
+ ///
-
+ /// Created s, if any, will be reported at this translation phase.
+ ///
-
+ /// The selected s that do not have any
+ /// are filtered one additional time to create a unique set. See for details on
+ /// how this unique set is formed.
+ ///
-
+ /// Each item in the selected set is translated into a .
+ ///
-
+ /// Each item is translated into a .
+ ///
-
+ /// The data set is translated into a object which will create the final generated
+ /// source.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void Initialize(IncrementalGeneratorInitializationContext initContext)
{
var methodSymbolsWithContext = initContext.SyntaxProvider.CreateSyntaxProvider(static (node, cancellationToken) =>
@@ -67,9 +95,6 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext)
var methodSymbolsWithMarshalInfo = methodSymbolsWithMarshalAndDiagnosticInfo.Where(static x => x.Diagnostics is null)
.Collect().SelectMany(static (symbolsWithMarshalInfo, cancellationToken) =>
{
- // this hash set will ensure each combination of a method symbol and marshaling behavior are unique
- // if the method is the generic FromFunctionPointer then the IMethodSymbol is used for comparison, otherwise the
- // INamedTypeSymbol (e.g., INativeAction<...>) is used for comparison
HashSet infoSet = new();
foreach (var symbolWithMarshalInfo in symbolsWithMarshalInfo)
{
diff --git a/MethodSymbolWithContext.cs b/MethodSymbolWithContext.cs
index cf8a807..6550183 100644
--- a/MethodSymbolWithContext.cs
+++ b/MethodSymbolWithContext.cs
@@ -17,6 +17,11 @@
namespace NativeGenericDelegatesGenerator
{
+ ///
+ /// Represents an (representing a native generic delegate method, such as
+ /// INativeAction.FromAction) paired with a (representing the method invocation
+ /// site in user code).
+ ///
internal readonly struct MethodSymbolWithContext
{
public readonly GeneratorSyntaxContext Context;
diff --git a/MethodSymbolWithMarshalAndDiagnosticInfo.cs b/MethodSymbolWithMarshalAndDiagnosticInfo.cs
index bd232f6..28f131a 100644
--- a/MethodSymbolWithMarshalAndDiagnosticInfo.cs
+++ b/MethodSymbolWithMarshalAndDiagnosticInfo.cs
@@ -18,6 +18,16 @@
namespace NativeGenericDelegatesGenerator
{
+ ///
+ /// Represents an (see ) with parsed custom marshaling
+ /// behavior or a reporting the parsing error.
+ ///
+ ///
+ ///
+ /// If is not , the other members of this object should not be considered as
+ /// valid, and they will not be used during source generation.
+ ///
+ ///
internal readonly struct MethodSymbolWithMarshalAndDiagnosticInfo
{
public readonly ImmutableArray? Diagnostics;
diff --git a/MethodSymbolWithMarshalInfo.cs b/MethodSymbolWithMarshalInfo.cs
index 9871fe1..7435135 100644
--- a/MethodSymbolWithMarshalInfo.cs
+++ b/MethodSymbolWithMarshalInfo.cs
@@ -9,11 +9,26 @@
// required.
using Microsoft.CodeAnalysis;
-using System.Collections.Immutable;
using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
namespace NativeGenericDelegatesGenerator
{
+ ///
+ /// Represents an (see ) with custom marshaling behavior.
+ ///
+ ///
+ ///
+ /// This type represents a method symbol and custom marshaling behaviors that may or may not be unique, but is suitable for
+ /// equality comparisons and hashing operations (e.g., use in a ). Equality and hashing combine an
+ /// (either or the representing the
+ /// containing interface) with the custom marshaling behavior. The is always compared using . If represents a generic overload of
+ /// FromFunctionPointer (e.g., INativeAction<string>.FromFunctionPointer<nint>), then equality and
+ /// hashing is performed using ; otherwise, the is used.
+ ///
+ ///
internal readonly struct MethodSymbolWithMarshalInfo : IEquatable
{
private readonly int Hash;
diff --git a/NativeGenericDelegateConcreteClassInfo.cs b/NativeGenericDelegateConcreteClassInfo.cs
index 0613dc8..83ab2cb 100644
--- a/NativeGenericDelegateConcreteClassInfo.cs
+++ b/NativeGenericDelegateConcreteClassInfo.cs
@@ -12,6 +12,10 @@
namespace NativeGenericDelegatesGenerator
{
+ ///
+ /// Represents a set of concrete classes that produce a native generic delegate with a unique signature, including custom
+ /// marshaling behaviors and calling conventions.
+ ///
internal readonly struct NativeGenericDelegateConcreteClassInfo
{
public readonly int ArgumentCount;
diff --git a/NativeGenericDelegateInfo.cs b/NativeGenericDelegateInfo.cs
index b1bfe59..e1a49d9 100644
--- a/NativeGenericDelegateInfo.cs
+++ b/NativeGenericDelegateInfo.cs
@@ -18,6 +18,9 @@
namespace NativeGenericDelegatesGenerator
{
+ ///
+ /// Represents basic information about a native generic delegate signature, including custom marshaling behaviors.
+ ///
internal readonly struct NativeGenericDelegateInfo
{
public readonly string ClassNamePrefix;
diff --git a/PartialImplementations.cs b/PartialImplementations.cs
index e5eac05..b8e0e2b 100644
--- a/PartialImplementations.cs
+++ b/PartialImplementations.cs
@@ -15,6 +15,10 @@
namespace NativeGenericDelegatesGenerator
{
+ ///
+ /// Represents the partial interface implementations for native generic delegates and the concrete class definitions which
+ /// implement those interfaces.
+ ///
internal readonly struct PartialImplementations
{
public readonly string ConcreteClassDefinitions;
diff --git a/PostInitialization.cs b/PostInitialization.cs
index be5ed73..df41f69 100644
--- a/PostInitialization.cs
+++ b/PostInitialization.cs
@@ -12,6 +12,9 @@
namespace NativeGenericDelegatesGenerator
{
+ ///
+ /// Represents the native generic delegate sources that will be added after the source generator has been initialized.
+ ///
internal static class PostInitialization
{
private static void BuildNativeAction(StringBuilder sb, string callConv)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..55a8c78
--- /dev/null
+++ b/README.md
@@ -0,0 +1,350 @@
+# NativeGenericDelegates
+
+__NativeGenericDelegates__ is a C# project designed to provide
+[delegate](https://learn.microsoft.com/en-us/dotnet/api/system.delegate?view=net-7.0)-like `class` types that are
+_[generic](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics)_ and can be used from _native_ code with
+_[platform invoke](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke)_ (P/Invoke). There is a historical
+caveat to .NET generics in that they cannot
+be used with P/Invoke. [Marshal.GetFunctionPointerForDelegate](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getfunctionpointerfordelegate?view=net-7.0#system-runtime-interopservices-marshal-getfunctionpointerfordelegate(system-delegate))
+in particular will throw an `ArgumentException` if passed a generic delegate
+type. This means that you are often left with no option but to create your own
+delegate types as-needed, and there is little to no room for code reusability.
+
+This project was inspired by a
+[StackOverflow question](https://stackoverflow.com/questions/26699394/c-sharp-getdelegateforfunctionpointer-with-generic-delegate)
+for which the correct solution was, in fact, to use the `DllImportAttribute`. However, there are still scenarios where it may be
+desirable to utilize generic delegates with P/Invoke and this project aims to facilitate those use cases.
+
+This solution uses an [incremental generator](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md)
+which creates a lot of boilerplate code. This may not be a good fit for every project, particularly if you are only using a small
+number of delegates.
+
+## What this project does
+
+This project provides a set of `interface`s that mirror the `System.Action` and `System.Func` delegate types with the following
+features:
+
+* Generic interface (e.g., `INativeAction`, `INativeFunc`, etc.)
+* Convert to `System.Action` and `System.Func` delegate types (`ToAction` and `ToFunc`)
+* Full interoperability with APIs consuming `Delegate` objects (via intermediary call to `ToAction` or `ToFunc`)
+* Passing delegate to unmanaged code (via `GetFunctionPointer`)
+* Construct delegates from managed and unmanaged functions with common interface
+* Non-dynamic invocation from managed code (via `Invoke`, no calls to `Delegate.DynamicInvoke`)
+* Define unmanaged function pointer calling convention
+* Optionally define marshaling behavior for return value and parameters
+* [Native AOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) deployment available (__new__ from v2.0.0)
+
+## About this version
+
+__v2.0.0__ is a complete rewrite of the _NativeGenericDelegates_ project. The previous version (v1.0.0) aimed to provide
+dynamically instanciated `System.Delegate` objects that implemented one of the provided interfaces, but relied on classes from
+`System.Reflection.Emit`, which is incompatible with some .NET platforms (those using native AOT deployment).
+
+This version instead aims to create classes that implement the provided interfaces at compile-time, using an incremental
+generator. As such, this version introduces __breaking changes__ in the API. ___Importantly___, the generated class objects are
+__NOT__ instances of the `System.Delegate` or `System.MulticastDelegate` types, and `/unsafe` is __required__.
+
+## How to use this project
+
+__You just DO WHAT THE FUCK YOU WANT TO.__
+
+As per the [license terms](COPYING.md), you are free to use this project however you see fit, but the following contains an
+overview of the public API that the code exposes.
+
+__IMPORTANT:__ This version __requires__ the `/unsafe` compile switch. A future version may not require unsafe code.
+
+### INativeAction and INativeFunc
+```C#
+INativeAction
+INativeFunc
+```
+
+There are 17 variations each of the `INativeAction` and `INativeFunc` interfaces that mirror `System.Action` and `System.Func`
+respectively. Similarly, `INativeAction` represents a method with no return value (`void` return type) and `INativeFunc` takes
+the return type as the final generic type parameter (`TResult`). For brevity, the shorthand ``/`` will be
+used to denote all 17 variations of the interface.
+
+#### Converting to and from Delegate
+
+___BREAKING CHANGE:__ Unlike v1.0.0, the instances of `INativeAction` and `INativeFunc` exposed by the API
+methods below are __NOT__ instances of `System.Delegate` or `System.MulticastDelegate`. Using the `as` or `is` operators to
+compare an instance with a delegate will __always fail__._
+
+Instances of `INativeAction` and `INativeFunc` are compatible with APIs that accept `System.Delegate`
+objects, but this requires an explicit call to `ToAction` or `ToFunc` (respectively).
+
+```C#
+private void Foo(int i) { }
+private void Bar(Delegate d) { }
+
+var foo = INativeAction.FromAction(Foo);
+Bar(foo.ToAction());
+```
+
+The conversion in the other direction depends on a call to `FromAction` or `FromFunc`:
+
+```C#
+INativeAction? nativeAction;
+if (d is Action action)
+{
+ nativeAction = INativeAction.FromAction(action);
+}
+else
+{
+ Action action = (Action)Delegate.CreateDelegate(typeof(Action), d.Target, d.Method);
+ nativeAction = INativeAction.FromAction(action);
+}
+SetNativeCallback(nativeAction.GetFunctionPointer());
+```
+
+#### Custom marshaling
+
+___BREAKING CHANGE:__ Unlike v1.0.0, there is no way to implicitly copy the custom marshaling behavior from the provided managed
+delegate. As such, `INativeAction.NoCustomMarshaling` and `INativeFunc.NoCustomMarshaling` have been removed._
+
+___BREAKING CHANGE:__ The `marshalReturnAs` and `marshalParamsAs` parameters have restrictions on their use that must be observed
+to correctly generate the custom marshaling behaviors. See below._
+
+The methods below permit you to specify custom marshaling behavior for the native generic delegates that you create. The default
+behavior when constructing these delegates using `FromAction` or `FromFunc` is to leave the marshaling behavior undefined by the
+generated delegate. This is the same behavior that you would get if you created your own delegate type and did not supply any
+`MarshalAs` attributes on the return value or the parameters. If the managed method represented by this delegate has explicit
+marshaling behavior defined, that behavior will still be preserved.
+
+When constructing a native generic delegate using `FromFunctionPointer`, custom marshaling behavior must be defined if you have
+[non-blittable](https://learn.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types) arguments, or if
+your managed and unmanaged parameters are not of the same type.
+
+The `marshalReturnAs` and `marshalParamsAs` parameters may be `null`, which will be used to represent the default behavior.
+Otherwise, these parameters are restricted in how they must be used. You may use inline `new` expression syntax, or you may
+reference a __readonly__ field with inline initialization.
+
+__IMPORTANT:__ If you use a field to define custom marshaling behavior, it __must__ be initialized inline. Assignment in a
+constructor will __NOT__ be detected by the source generator and will not be reflected in the generated delegate type. This is
+by-design and will not be changed in future versions.
+
+```C#
+private static readonly MarshalAsAttribute[] nativeCallbackMarshaling = new[] { new MarshalAsAttribute(UnmanagedType.LPWStr) }; // okay, inline initialization of readonly field
+
+static ThisClass()
+{
+ nativeCallbackMarshaling = new[] { new MarshalAsAttribute(UnmanagedType.LPUTF8Str) }; // NOT DETECTED BY GENERATOR, THIS VALUE IS IGNORED
+}
+
+var nativeAction = INativeAction.FromFunctionPointer(GetNativeCallback(), new[] { new MarshalAsAttribute(UnmanagedType.LPWStr) }); // okay, inline new expression
+var nativeAction2 = INativeAction.FromFunctionPointer(GetNativeCallback(), nativeCallbackMarshaling); // okay, reference to readonly field - ONLY REFLECTS VALUE FROM INLINE INITIALIZATION
+
+var localMarshaling = new[] { new MarshalAsAttribute(UnmanagedType.LPWStr) };
+var nativeAction3 = INativeAction.FromFunctionPointer(GetNativeCallback(), localMarshaling); // compile-time error: must use inline new expression, null expression, or readonly field reference
+```
+
+#### FromAction and FromFunc
+```C#
+static INativeAction INativeAction.FromAction(Action action, [optional] CallingConvention callingConvention = CallingConvention.Winapi)
+static INativeAction INativeAction.FromAction(Action action, [optional] MarshalAsAttribute?[]? marshalParamsAs = null, [optional] CallingConvention callingConvention = CallingConvention.Winapi)
+static INativeFunc INativeFunc.FromFunc(Func func, [optional] MarshalAsAttribute? marshalReturnAs = null, [optional] MarshalAsAttribute?[]? marshalParamsAs = null, [optional] CallingConvention callingConvention = CallingConvention.Winapi)
+```
+
+___BREAKING CHANGE:__ In v1.0.0, these methods were represented as `FromDelegate` and `FromMethod`. It is no longer supported to
+create a native generic delegate directly from a `System.Delegate` or `System.Reflection.MethodInfo`._
+
+Creates a native generic delegate with the same signature as the interface that will invoke the same method (and target, if any)
+as the given delegate.
+
+`action`/`func` is the delegate from which the invocation method and target will be copied.
+
+`marshalReturnAs` is an optional `MarshalAsAttribute` that defines the custom marshaling behavior of the managed delegate return
+value, if any. `FromAction` omits this parameter as there is no return value.
+
+`marshalParamsAs` is an optional array of `MarshalAsAttribute?` that defines the custom marshaling behavior of the managed
+delegate parameters, if any. `INativeAction.FromAction` omits this parameter as the delegate does not accept any parameters. If
+this array is shorter than the number of parameters, the remaining entries are assumed as `null`.
+
+`callingConvention` is the calling convention of the unmanaged function pointer returned by
+[GetFunctionPointer](#getfunctionpointer). If calling convention is `CallingConvention.Winapi`, then the default platform calling
+convention is used instead: `CallingConvention.StdCall` on Windows and `CallingConvention.Cdecl` on all other platforms.
+
+If any `MarshalAsAttribute?` is `null` (for `marshalReturnAs`, any of the entries in the `marshalParamsAs` array, or the
+`marshalParamsAs` array itself), then no custom marshaling behavior is applied for the respective parameter or return value. Any
+marshaling behavior defined by the managed method that this native generic delegate represents will still be preserved, but the
+marshaling behavior will not be defined in the generated managed delegate type.
+
+_Returns:_ An object representing a managed delegate with the requested signature and marshaling behavior, which implements the
+native generic delegate interface.
+
+_Example:_
+
+```C#
+public static void OnNativeEvent(int eventID)
+{
+ Console.WriteLine($"Native event {eventID} raised.");
+}
+
+var nativeEventHandler = INativeAction.FromAction(OnNativeEvent);
+SetNativeEventCallback(nativeEventHandler.GetFunctionPointer());
+```
+
+_See also:_
+[FromFunctionPointer](#fromfunctionpointer),
+[GetFunctionPointer](#getfunctionpointer),
+[Invoke](#invoke),
+[ToAction](#toaction-and-tofunc),
+[ToFunc](#toaction-and-tofunc)
+
+#### FromFunctionPointer
+```C#
+static INativeAction INativeAction.FromFunctionPointer(nint functionPtr, [optional] CallingConvention callingConvention = CallingConvention.Winapi)
+static INativeAction INativeAction.FromFunctionPointer(nint functionPtr, [optional] MarshalAsAttribute?[]? marshalParamsAs = null, [optional] CallingConvention callingConvention = CallingConvention.Winapi)
+static INativeAction INativeAction.FromFunctionPointer(nint functionPtr, MarshalAsAttribute[] marshalParamsAs, [optional] CallingConvention callingConvention = CallingConvention.Winapi)
+static INativeFunc INativeFunc.FromFunctionPointer(nint functionPtr, [optional] MarshalAsAttribute? marshalReturnAs = null, [optional] MarshalAsAttribute?[]? marshalParamsAs = null, CallingConvention callingConvention = CallingConvention.Winapi)
+static INativeFunc INativeFunc.FromFunctionPointer(nint functionPtr, MarshalAsAttribute marshalReturnAs, MarshalAsAttribute[] marshalParamsAs, [optional] CallingConvention callingConvention = CallingConvention.Winapi)
+```
+
+___BREAKING CHANGE:__ In v1.0.0, these methods accepted unmanaged function pointers as the first argument, which is no longer
+supported._
+
+Creates a native generic delegate with the same signature as the interface that will invoke an unmanaged function pointer.
+
+The overloads that accept a second set of type parameters (``/``) permit you to specify both the _managed_
+delegate parameter types (``/``) and the _unmanaged_ parameter types (``/``). For
+example, you might use `INativeAction.FromFunctionPointer` to describe a native generic delegate that accepts a
+`System.String` parameter in managed code, and a `const char*` (`nint`) parameter in unmanaged code. The custom marshaling
+behavior for these overloads is not optional.
+
+`functionPtr` is the unmanaged function pointer that will be invoked by the delegate.
+
+`marshalReturnAs` is an optional `MarshalAsAttribute` that defines the custom marshaling behavior of the managed delegate return
+value, if any. `FromAction` omits this parameter as there is no return value.
+
+`marshalParamsAs` is an optional array of `MarshalAsAttribute?` that defines the custom marshaling behavior of the managed
+delegate parameters, if any. `INativeAction.FromAction` omits this parameter as the delegate does not accept any parameters. If
+this array is shorter than the number of parameters, the remaining entries are assumed as `null`.
+
+`callingConvention` is the calling convention of the unmanaged function pointer returned by
+[GetFunctionPointer](#getfunctionpointer). If calling convention is `CallingConvention.Winapi`, then the default platform calling
+convention is used instead: `CallingConvention.StdCall` on Windows and `CallingConvention.Cdecl` on all other platforms.
+
+_Returns:_ An object representing a managed delegate with the requested signature and marshaling behavior, which implements the
+native generic delegate interface.
+
+_Example:_
+
+```C#
+nint pMyDllFunc = GetProcAddress(myDllHandle, "MyDllFunc"); // get some function pointer from native code
+var myDllFuncHandler = INativeFunc.FromFunctionPointer
+(
+ pMyDllFunc,
+ new MarshalAsAttribute(UnmanagedType.LPUTF8Str),
+ new[] { new MarshalAsAttribute(UnmanagedType.I4) }
+);
+Console.WriteLine(myDllFuncHandler.Invoke(42));
+```
+
+_Where possible, `DllImportAttribute` or `LibraryImportAttribute` are likely a better option to import a method from an unmanaged
+library. This method may be useful if an unmanaged library exposes method handles that are not exported or are expensive to load,
+or if you need to invoke the method from managed code while maintaining an unmanaged pointer to the method._
+
+_See also:_
+[FromAction](#fromaction-and-fromfunc),
+[FromFunc](#fromaction-and-fromfunc),
+[GetFunctionPointer](#getfunctionpointer),
+[Invoke](#invoke),
+[ToAction](#toaction-and-tofunc),
+[ToFunc](#toaction-and-tofunc)
+
+#### GetFunctionPointer
+```C#
+nint INativeAction.GetFunctionPointer()
+nint INativeFunc.GetFunctionPointer()
+```
+
+Converts the native generic delegate into a function pointer that is callable from unmanaged code.
+
+You must keep a managed reference to the native generic delegate instance for the lifetime of the unmanaged function pointer.
+
+_Returns:_ A value that can be passed to unmanaged code, which, in turn, can use it to call the underlying managed delegate.
+
+_Example:_
+
+```C#
+public static void OnNativeEvent(int eventID)
+{
+ Console.WriteLine($"Native event {eventID} raised.");
+}
+
+var nativeEventHandler = INativeAction.FromAction(OnNativeEvent);
+SetNativeEventCallback(nativeEventHandler.GetFunctionPointer());
+```
+
+_See also:_
+[FromAction](#fromaction-and-fromfunc),
+[FromFunc](#fromaction-and-fromfunc),
+[FromFunctionPointer](#fromfunctionpointer),
+[Invoke](#invoke),
+[ToAction](#toaction-and-tofunc),
+[ToFunc](#toaction-and-tofunc)
+
+#### Invoke
+```C#
+void INativeAction.Invoke(T..16 t..16)
+TResult INativeFunc.Invoke(T..16 t..16)
+```
+
+Invokes the managed or unmanaged method that this native generic delegate represents with the given parameters.
+
+_Note:_ This does __not__ call `Delegate.DynamicInvoke` and does not incur the performance penalty associated with that method.
+
+_Returns:_ Nothing (`INativeAction`) or `TResult` (`INativeFunc`).
+
+_Example:_
+
+```C#
+var printPoint = INativeAction.FromAction
+(
+ (int x, int y) => Console.WriteLine($"Point {{ X = {x}, Y = {y} }}")
+);
+printPoint.Invoke(420, 69);
+```
+
+_See also:_
+[FromAction](#fromaction-and-fromfunc),
+[FromFunc](#fromaction-and-fromfunc),
+[FromFunctionPointer](#fromfunctionpointer),
+[GetFunctionPointer](#getfunctionpointer),
+[ToAction](#toaction-and-tofunc),
+[ToFunc](#toaction-and-tofunc)
+
+#### ToAction and ToFunc
+```C#
+Action INativeAction.ToAction()
+Func INativeFunc.ToFunc()
+```
+
+Creates a `System.Action` or `System.Func` with the same target and method as the native generic delegate.
+
+_Note:_ The returned delegate does __NOT__ implement the native generic delegate interface. You can convert back to an equivalent
+native generic delegate using [FromAction](#fromaction-and-fromfunc) or [FromFunc](#fromaction-and-fromfunc).
+
+_Returns:_ The requested delegate.
+
+_Example:_
+
+```C#
+public static void InvokeAction(Action action, int x, int y)
+{
+ action(x, y);
+}
+
+var printPoint = INativeAction.FromAction
+(
+ (int x, int y) => Console.WriteLine($"Point {{ X = {x}, Y = {y} }}")
+);
+InvokeAction(printPoint.ToAction(), 420, 69);
+```
+
+_See also:_
+[FromAction](#fromaction-and-fromfunc),
+[FromFunc](#fromaction-and-fromfunc),
+[FromFunctionPointer](#fromfunctionpointer),
+[GetFunctionPointer](#getfunctionpointer),
+[Invoke](#invoke)
diff --git a/RuntimeMarshalAsAttributeArrayCollection.cs b/RuntimeMarshalAsAttributeArrayCollection.cs
index c0d61cf..ee6434e 100644
--- a/RuntimeMarshalAsAttributeArrayCollection.cs
+++ b/RuntimeMarshalAsAttributeArrayCollection.cs
@@ -1,9 +1,20 @@
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Runtime.InteropServices;
using System.Text;
namespace NativeGenericDelegatesGenerator
{
+ ///
+ /// Represents a unique set of arrays at runtime.
+ ///
+ ///
+ ///
+ /// This class generates source that will represent the attribute arrays at runtime. These arrays are then used at runtime
+ /// for the type comparisons required to create a native generic delegate. These arrays are populated using a .
+ ///
+ ///
internal sealed class RuntimeMarshalAsAttributeArrayCollection
{
private readonly Dictionary attributes = new();
diff --git a/RuntimeMarshalAsAttributeCollection.cs b/RuntimeMarshalAsAttributeCollection.cs
index bc2dc56..6e69cd1 100644
--- a/RuntimeMarshalAsAttributeCollection.cs
+++ b/RuntimeMarshalAsAttributeCollection.cs
@@ -1,9 +1,19 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
using System.Text;
namespace NativeGenericDelegatesGenerator
{
+ ///
+ /// Represents a unique set of s at runtime.
+ ///
+ ///
+ ///
+ /// This class generates source that will represent teh attributes at runtime. These attributes are then used at runtime for
+ /// the type comparisons required to create a native generic delegate, including the runtime array comparisons generated
+ /// using a that references this collection.
+ ///
+ ///
internal sealed class RuntimeMarshalAsAttributeCollection
{
private readonly Dictionary attributes = new();