# An Explanation of Shared Types

The PluginLoader API uses the term "shared types". This document explains what it means.

```csharp
PluginLoader.CreateFromAssemblyFile("./plugins/MyPlugin/MyPlugin1.dll",
    sharedTypes: new [] { typeof(ILogger) });
    
// versus

PluginLoader.CreateFromAssemblyFile("./plugins/MyPlugin/MyPlugin1.dll",
    config => config.PreferSharedTypes = true);
```

## Concepts

First, a quick overview of essential concepts.

#### Type identity

Type identity what makes a class/struct/enum unique. It is defined by the combination of type name (which includes its namespace), assembly name, 
assembly public key token, and assembly version. You can inspect a type's identity in .NET by looking at System.Type.AssemblyQualifiedName. 

For example,

```csharp
typeof(ILogger).AssemblyQualifiedName 
   => "Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.Abstractions, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"
```

Element                    | Value
---------------------------|------------------
Type name                  | Microsoft.Extensions.Logging.ILogger
Assembly name              | Microsoft.Extensions.Logging.Abstractions
Assembly version           | 2.2.0.0
Assembly public key token  | adb9793829ddae60

> Simplification: we're going to ignore the "Culture" part of the fully qualified name for now.

#### Diamond dependencies

The [diamond dependency problem][dep-hell] is when a library A depends on libraries B and C, both B and C depend on library D, 
but B requires version D.1 and C requires version D.2.

[dep-hell]: https://en.wikipedia.org/wiki/Dependency_hell

<img width="250" title="Diamond dependency" src="https://imgur.com/WEA8X1U.png" />

You could solve this problem by

1. Choosing D.1
1. Choosing D.2
1. Choosing both

#### Type unification (option 2)

Type unification is .NET's solution for the diamond dependency problem. In the simple example above,
.NET's build system picks the higher version (D.2) and writes this into the application manifest 
(the .deps.json or .config file in build output.) Then, when the application is running and encounters usages of D.1, .NET binds
the usage to D.2 instead.

In other words, .NET will ignore assembly version when evaluating type identity.

* Type name
* Assembly name
* ~~Assembly version~~ _this part gets ignored_
* Assembly public key token

Why is this done? It allows **type exchange** so code can share instances of types even if the code was originally compiled 
with different dependency versions.

```csharp
var instanceOfD = new D(); // D.2
new B().DoSomethingWith(instanceOfD); // Library B was compiled to expect D.1, but type unification makes it work with D.2
new C().DoSomethingWith(instanceOfD); 
```

## But what if...

There are two common problems with type unification.

1. Breaking changes: what if library B depends on a behavior of D.1 that changed in D.2 and breaks B? Conversely, what library C uses a new API added in D.2, 
   but we force the app to use D.1 instead?
2. Static vs dynamic: what if my app has a plugin system with dynamic dependencies? 

## This library's answer...

By default, this `PluginLoader` does not unify any types. This means you can have **multiple versions of the same assembly** 
loaded in separate plugins.

```csharp
PluginLoader.CreateFromAssemblyFile("./plugins/MyPlugin/MyPlugin1.dll")
```

This can make working with a plugin difficult because it breaks **type exchange**, so the `sharedTypes` list API
is provided to allow you to select which types you want to make sure are unified between the plugin and the 
application loading the plugin (aka the host).
```csharp
PluginLoader.CreateFromAssemblyFile("./plugins/MyPlugin/MyPlugin1.dll",
    sharedTypes: new [] { typeof(ILogger) });
```

Finally, you can invert the default completely to **always attempt to unify** by setting `PreferSharedTypes`. In this mode,
the assembly version provided by the host uses is always used.
```csharp
PluginLoader.CreateFromAssemblyFile("./plugins/MyPlugin/MyPlugin1.dll",
    config => config.PreferSharedTypes = true);

// In older versions of the library, this API was found on PluginLoaderOptions
PluginLoader.CreateFromAssemblyFile("./plugins/MyPlugin/MyPlugin1.dll",
    PluginLoaderOptions.PreferSharedTypes);
```