Skip to content

Commit

Permalink
📚 Document finding converter plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
pleonex committed Nov 30, 2023
1 parent e7edbcc commit 04760de
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 2 deletions.
71 changes: 69 additions & 2 deletions docs/articles/plugins/locate-types.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,76 @@
# Locate types

After [loading external .NET assemblies](./load-assembly.md) containing
implementation of _formats_ and _converters_, the application can get a list of
them via `ConverterLocator`.

> [!NOTE]
> This is only needed if the application does not know in advance the converter
> to use. It can present the list to the user so it can choose. Or it can get
> the converter names from a configuration file and later find the actual type
> via reflection. For instance for generic Tinke-like applications.
## TypeLocator

The `TypeLocator` is a _singleton_ class that p
The `TypeLocator` provides features to find types that implement or inherit a
given base type. It searches in the **loaded assemblies** of an
`AssemblyLoadContext` instance. The default _singleton_ instance is accesible
via `TypeLocator.Default` and it uses `AssemblyLoadContext.Default`. Normally
you don't need to create your own instance.

> [!NOTE]
> .NET loads assemblies lazily, when a code to run needs them. If you need a
> deterministic search consider loading every assembly from the application
> path. See
> [Load from executing directory](./load-assembly.md#load-from-executing-directory)
> for more information.
To find a list of types that inherit a given base class or implements an
interface use the method
[`FindImplementationsOf(Type)`](<xref:Yarhl.Plugins.TypeLocator.FindImplementationsOf(System.Type)>).
It searches for final types, that is: **classes that are public and not
abstract**. It returns information for each of these types in the _record_
[`TypeImplementationInfo`](xref:Yarhl.Plugins.TypeImplementationInfo)

For instance to find every _format_ in the loaded asssemblies use:

[!code-csharp[FindFormats](../../../src/Yarhl.Examples/Plugins/LocateTypesExamples.cs?name=FindFormats)]

The case of a _generic base type_ is special as types may implemented it
multiple. For instance a _class_ may implement `IConverter<Po, BinaryFormat>`
**and** `IConverter<BinaryFormat, Po>`. Using the _generic type definition_
(`typeof(IConverter<,>)`) to find types will throw an exception. Use this method
if you are searching for a specific implementation, like
`typeof(IConverter<Po, BinaryFormat>)`

Use the method
[`FindImplementationsOfGeneric(Type)`](<xref:Yarhl.Plugins.TypeLocator.FindImplementationsOfGeneric(System.Type)>)
to get a list of types implementing the **generic base type definition** with
any type arguments. For instance in the previous example calling
`FindImplementationsOfGeneric(typeof(IConverter<,>))` will return two results
for that class. One for `IConverter<Po, BinaryFormat>` and a second for
`IConverter<BinaryFormat, Po>`. The return type is the _record_
[`GenericTypeImplementationInfo`](xref:Yarhl.Plugins.GenericTypeImplementationInfo)

[!code-csharp[FindConverters](../../../src/Yarhl.Examples/Plugins/LocateTypesExamples.cs?name=FindConverters)]

## ConverterLocator

TODO
The [`ConverterLocator`](xref:Yarhl.Plugins.FileFormat.ConverterLocator) class
provides a cache of formats and converters found in the loaded assemblies.
During initialization (first use) it will use `TypeLocator` to find every format
and converter types. The `Default` singleton instance use `TypeLocator.Default`.
You can pass a custom `TypeLocator` via its public constructor.

The properties
[`Converters`](xref:Yarhl.Plugins.FileFormat.ConverterLocator.Converters) and
[`Formats`](xref:Yarhl.Plugins.FileFormat.ConverterLocator.Formats) provides a
list of the types found, so there is no need to re-scan the assemblies each
time.

> [!NOTE]
> If a new assembly is loaded in the `AssemblyLoadContext`, the
> `ConverterLocator` will need to performn a re-scan to find the new types. Make
> sure to call
> [`ConverterLocator.ScanAssemblies()`](xref:Yarhl.Plugins.FileFormat.ConverterLocator.ScanAssemblies)
> after loading new assemblies.
52 changes: 52 additions & 0 deletions src/Yarhl.Examples/Plugins/LocateTypesExamples.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2023 SceneGate

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
namespace Yarhl.Examples.Plugins;

using Yarhl.FileFormat;
using Yarhl.Plugins;

public static class LocateTypesExamples
{
public static void FindFormats()
{
#region FindFormats
TypeImplementationInfo[] formatsInfo = TypeLocator.Default
.FindImplementationsOf(typeof(IFormat))
.ToArray();

Console.WriteLine(formatsInfo[0].Name); // e.g. Yarhl.IO.BinaryFormat
Console.WriteLine(formatsInfo[0].Type); // e.g. Type object for BinaryFormat
#endregion
}

public static void FindConverters()
{
#region FindConverters
GenericTypeImplementationInfo[] convertersInfo = TypeLocator.Default
.FindImplementationsOfGeneric(typeof(IConverter<,>))
.ToArray();

Console.WriteLine(convertersInfo[0].Name); // e.g. Yarhl.Media.Text.Binary2Po
Console.WriteLine(convertersInfo[0].Type); // e.g. Type object for Yarhl.Media.Text.Binary2Po
Console.WriteLine(convertersInfo[0].GenericBaseType); // e.g. Type IConverter<BinaryFormat, Po>
Console.WriteLine(convertersInfo[0].GenericTypeParameters); // e.g. [BinaryFormat, Po]
#endregion
}
}
1 change: 1 addition & 0 deletions src/Yarhl.Examples/Yarhl.Examples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<ItemGroup>
<ProjectReference Include="..\Yarhl\Yarhl.csproj" />
<ProjectReference Include="..\Yarhl.Media.Text\Yarhl.Media.Text.csproj" />
<ProjectReference Include="..\Yarhl.Plugins\Yarhl.Plugins.csproj" />
</ItemGroup>

</Project>

0 comments on commit 04760de

Please sign in to comment.