Создайте экземпляр Generi c Type из сборки другого контекста в C#, используя refect - PullRequest
0 голосов
/ 20 апреля 2020

У меня есть Type из некоторых AssemblyContext (код плагина). Я знаю, что та же сборка также загружается в контексте по умолчанию. Я хочу иметь метод, который возвращает объект типа из соответствующей сборки в контексте по умолчанию.

Вот мой код

public object GetService(Type serviceType, IServiceProvider provider) {
    try
    {
        // If same context
        var defaultService = provider.GetService(serviceType);
        if (defaultService != null)
        {
            return defaultService;
        }
    }
    catch (InvalidOperationException ex) { }

    /// The <see cref="serviceType"/> might come from an external assembly.
    /// Try to find a matching type within the assemblies loaded with the 
    /// <see cref="Provider"/>'s <see cref="AssemblyLoadContext"/>.
    var providerContext =
        AssemblyLoadContext.GetLoadContext(provider.GetType().Assembly);
    var resolvedAssembly = FindBestMatch(providerContext.Assemblies.ToList(),
        serviceType.Assembly.GetName());
    Type resolvedType = null;

    if (serviceType.IsGenericType)
    {
        resolvedType = resolvedAssembly
            .GetType(serviceType.GetGenericTypeDefinition().FullName, throwOnError: true)
            .MakeGenericType(serviceType.GetGenericArguments());
    }
    else
    {
        resolvedType = resolvedAssembly.GetType(serviceType.FullName, true);
    }
    var resolvedService = provider.GetService(resolvedType);
    return ImpromptuInterface.Impromptu.DynamicActLike(resolvedService, serviceType);
}

FindBestMatch просто выглядит для сборки, хотя список -

Assembly FindBestMatch(IReadOnlyCollection<Assembly> choices, AssemblyName hint)
{
    if (choices == null) throw new ArgumentNullException(nameof(choices));
    if (hint == null) throw new ArgumentNullException(nameof(hint));

    return choices.FirstOrDefault(choice => choice.GetName() == hint)
        ?? choices.FirstOrDefault(choice => choice.GetName().FullName == hint.FullName)
        ?? choices.FirstOrDefault(choice => choice.GetName().Name == hint.Name);
}

Теперь он отлично работает для не-дженериков. Для таких обобщений, как ILogger<T>, он создает объект типа Logger<T>, но выдает RunTimeBinderException при попытке вызвать методы для этих объектов. Например, для регистратора сообщение об исключении гласит: «« Регистратор »не содержит определения для« LogDebug »».

Итак:

  1. что здесь происходит?
  2. есть ли лучший способ добиться этого?
...