我正在尝试制作一个可以动态加载和卸载售后程序集并创建该程序集中定义的类型的对象的应用程序,但是当对象具有迭代器方法时我遇到了问题。
和我在一起——最小的、可重现的示例有点大,因为它有多个部分。我将分三个阶段进行解释。
阶段1
这是没有插件架构的基本结构。在这里将所有这些都集中到一个组件中只是为了说明我要使用的结构。
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
主机.exe
main
ConcreteHostObject
插件.dll
ConcretePluginObject
我有一些执行此操作的激活码:
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
类型名称中的后缀,Plugin.ConcretePluginObject+<GetStrings>d__1
但我认为它与迭代器方法有关。我还浏览了这些文档,尤其是关于迭代器方法要求的部分,但它没有说明序列化要求。
有人可以解释出了什么问题以及我能做些什么来解决它吗?
重要要求
这是一个最小的、可重现的示例。但在我的实际插件中,该GetStrings
方法实际上是一个迭代器方法,它像协程一样工作,这意味着从 using 切换到using不是可接受的解决方法。没有字符串的集合,也没有数组。这确实是一个诚实的迭代器方法,它利用协程并像协程一样工作。IEnumerable<string>
string[]
yield