Я пытаюсь создать приложение, которое может динамически загружать и выгружать сборку стороннего производителя и создавать объект типа, определенного в этой сборке, но у меня возникают проблемы, когда у объекта есть метод итератора.
Оставайтесь со мной - минимальный воспроизводимый пример немного большой, потому что состоит из нескольких частей. Я объясню это в три этапа.
Фаза 1
Вот базовая c структура без имеющейся архитектуры плагина. Все это объединено в одну сборку, чтобы проиллюстрировать структуру, к которой я иду.
using API;
namespace API
{
public interface IHostObject
{
string Name { get; set; }
}
public interface IPluginObject
{
void DoSomething(API.IHostObject hostObject);
}
}
namespace Plugin
{
class ConcretePluginObject : API.IPluginObject
{
void IPluginObject.DoSomething(IHostObject hostObject)
{
System.Console.WriteLine(hostObject.Name);
}
}
}
namespace Host
{
class ConcreteHostObject : API.IHostObject
{
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
IHostObject hostObject = new ConcreteHostObject() { Name = "Hosty McHostface" };
IPluginObject pluginObject = new Plugin.ConcretePluginObject();
pluginObject.DoSomething(hostObject);
}
}
}
Фаза 2
Затем я разделил этот проект на три части, чтобы создать архитектуру плагина.
API.dll
API.IHostObject
API.IPluginObject
Host.exe
Plugin.dll
У меня есть активация код, который делает это:
using System;
using API;
namespace Host
{
class ConcreteHostObject : MarshalByRefObject, API.IHostObject
{
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
var appDir = AppDomain.CurrentDomain.BaseDirectory;
var pluginsDir = System.IO.Path.Combine(appDir, "Plugins");
var appDomainSetup = new AppDomainSetup {
ApplicationName = "",
ShadowCopyDirectories = "true",
ApplicationBase = pluginsDir,
CachePath = "VSSCache"
};
AppDomain apd = AppDomain.CreateDomain("NewZealand", null, appDomainSetup);
API.IPluginObject pluginObject = (API.IPluginObject)apd.CreateInstance("Plugin", "Plugin.ConcretePluginObject").Unwrap();
IHostObject hostObject = new ConcreteHostObject() { Name = "Hosty McHostface" };
pluginObject.DoSomething(hostObject);
}
}
}
Пока все это прекрасно работает.
Этап 3 - Вот проблема
У меня сложилось впечатление, что пока я только получить доступ к объектам через интерфейсы, которые определены в общей сборке API, что все будет хорошо. Но теперь у меня возникают проблемы, когда я добавляю IEnumerable<string>
функцию в свой IPluginObject
.
public interface IPluginObject
{
void DoSomething(API.IHostObject hostObject);
IEnumerable<string> GetStrings(); // Added this
}
И это реализовано так:
using System;
using System.Collections.Generic;
using API;
namespace Plugin
{
class ConcretePluginObject : MarshalByRefObject, API.IPluginObject
{
void IPluginObject.DoSomething(IHostObject hostObject)
{
System.Console.WriteLine(hostObject.Name);
}
public IEnumerable<string> GetStrings() // Added this iterator method
{
yield return "one";
yield return "two";
yield return "three";
}
}
}
Теперь, когда я звоню pluginObject.GetStrings()
, я получаю исключение:
System.Runtime.Serialization.SerializationException
HResult=0x8013150C
Message=Type 'Plugin.ConcretePluginObject+<GetStrings>d__1' in Assembly 'Plugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
Source=mscorlib
StackTrace:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at API.IPluginObject.GetStrings()
at Host.Program.Main(String[] args) in E:\Dev\Test\PluginTest\Host\Program.cs:line 27
Я думал, что это сработает, но, похоже, что-то в итераторе (функция, которая возвращает IEnumerable и использует ключевого слова yield
, чтобы это перестало работать.
Что здесь происходит?
Я признаю, что не могу понять смысл суффикса d__1
в типе name Plugin.ConcretePluginObject+<GetStrings>d__1
но я думаю, что это как-то связано с методом итератора. Я также просмотрел эти документы , особенно часть, касающуюся требований к методам итераторов, но ничего не говорится о требованиях к сериализации.
Может кто-нибудь объяснить, что пошло не так и что я могу сделать, чтобы это исправить?
Важное требование
Это Минимальный воспроизводимый пример . Но в моем настоящем плагине метод GetStrings
на самом деле является методом итератора, который работает как сопрограмма, то есть не приемлемый обходной путь для перехода от использования IEnumerable<string>
к использованию string[]
. Здесь нет коллекции строк и нет массива. Это действительно честный итерационный метод, который использует yield
и работает как сопрограмма.