加载未知类型

时间:2013-10-17 17:22:28

标签: c# reflection mef

我有动态加载的类(使用mef),它们是某种处理程序(这些工作正常)。

这些处理程序可以接收(有一个接收数据包并返回一个数据包的方法)数据包,它们都实现相同的接口(比如说IPacket)并返回一个答案(也就是一个IPacket)。

我收到这些数据包(通过tcp连接),我的程序不熟悉特定的类(虽然它是一个已知的接口--IPacket,但是一个不同的实现)。

因此,当我尝试反序列化数据包(将其交给处理程序)时,我得到一个异常。

  • 我将序列化为字节,反序列化为对象(使用二进制序列化器)*

我可以访问数据包实现的唯一方法应该是动态的,因为dll存储在我可以访问的文件夹中。

我认为我可以使用Assembly.LoadFrom来熟悉我的程序数据包,因为我甚至不需要实例化它们,只需反序列化(获取接口的实例)并移交给处理程序然后,它将返回一个答案,我会再次发送。

但它不起作用..

我假设我需要找到一种方法在运行时添加对这些dll的引用,然后我的程序将识别它们。(我认为可能在包类上使用Export(typeof()..)帮忙,不是吗?)

我在尝试反序列化时遇到的异常是找不到类名 ..

*我已经编辑了这个主题,我希望它有点清楚,谢谢=]


  • 编辑:

我并不是说这可以用mef来解决,我只是说它可以。 它绝对可以用反射来解决。我有一个文件夹,其中包含我希望程序在运行时识别的所有类,我只需要在运行时将其“加载”它们,就好像我在同一个文件夹中添加了对dll的引用。

基本上我需要做的是:

从文件夹加载某个接口的所有实现(本例中为IPacket)。我不需要实例化它们,而只需要将它们作为变量接收,而不会得到这样的类型不在我的项目中的异常。


所以我找到了这个片段:

static constructor() {
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

}

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
    Assembly ayResult = null;
    string sShortAssemblyName = args.Name.Split(',')[0];
     Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
     foreach (Assembly ayAssembly in ayAssemblies) {
        if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) {
             ayResult = ayAssembly;
             break;
        }
     }
     return ayResult;
}

它似乎接近我正在寻找的但我实际上并不理解这一点。 有没有办法中断这个,以便它只加载某个文件夹中的dll? 那我的程序会不会熟悉dll?

此外,我们非常感谢对代码的解释。

2 个答案:

答案 0 :(得分:1)

MEF肯定会帮助你,但不仅如此。您必须使用ISerializationSurrogate。下面的大多数解释都可以找到here

因此,给定以下数据包接口定义:

public interface IPacket
{
    string GetInfo();
}

您有以下实现,驻留在他们自己的程序集中:

[Export(typeof(IPacket))]
class FirstPacket : IPacket
{
    public FirstPacket()
    {
        Name = "Joe";
    }

    public string Name { get; set; }

    public string GetInfo()
    {
        return "Name: " + Name;
    }
}

[Export(typeof(IPacket))]
class SecondPacket : IPacket
{
    public SecondPacket()
    {
        Measurement = 42.42m;
    }

    public decimal Measurement { get; set; }

    public string GetInfo()
    {
        return "Measurement: " + Measurement;
    }
}

现在我们将定义另一个界面,例如:

public interface IPacketSurrogateProvider
{
    void AddSurrogate(SurrogateSelector toSelector);
}

匹配的实现,在定义具体数据包的同一个程序集中:

[Export(typeof(IPacketSurrogateProvider))]
class FirstPacketSurrogateProvider : IPacketSurrogateProvider, ISerializationSurrogate
{
    public void AddSurrogate(SurrogateSelector toSelector)
    {
        toSelector.AddSurrogate(typeof(FirstPacket), new StreamingContext(StreamingContextStates.All), this);
    }

    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Name", ((FirstPacket)obj).Name);
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        ((FirstPacket)obj).Name = info.GetString("Name");

        return obj;
    }
}

[Export(typeof(IPacketSurrogateProvider))]
class SecondPacketSurrogateProvider : IPacketSurrogateProvider, ISerializationSurrogate
{
    public void AddSurrogate(SurrogateSelector toSelector)
    {
        toSelector.AddSurrogate(typeof(SecondPacket), new StreamingContext(StreamingContextStates.All), this);
    }

    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Measurement", ((SecondPacket)obj).Measurement);
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        ((SecondPacket)obj).Measurement = info.GetDecimal("Measurement");

        return obj;
    }
}

现在,在一个程序集中,它引用了带有接口的程序集,但没有引用具有实现的程序集,并且具有与上述两者相同的部署文件夹:

public static void Test()
{
    var container = new CompositionContainer(new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)));

    var packets = container.GetExportedValues<IPacket>().ToArray();
    var packetSurrogateProviders = container.GetExportedValues<IPacketSurrogateProvider>();

    var surrogateSelector = new SurrogateSelector();
    foreach (var provider in packetSurrogateProviders)
    {
        provider.AddSurrogate(surrogateSelector);
    }

    var deserializedPackets = new IPacket[] { };
    using (var stream = new MemoryStream())
    {
        var formatter = new BinaryFormatter {SurrogateSelector = surrogateSelector};

        formatter.Serialize(stream, packets);

        stream.Position = 0;

        deserializedPackets = (IPacket[])formatter.Deserialize(stream);
    }

    foreach (var packet in deserializedPackets)
    {
        Console.WriteLine("Packet info: {0}", packet.GetInfo());
    }
}

产生:

  

包信息:姓名:Joe

     

包信息:测量:42.42

答案 1 :(得分:0)

请查看this example,尤其是BindToType方法。我想你可以检查装配是否已加载。如果不是,则使用反射(或MEF,如果您愿意)加载它。然后只需返回base.BindToType即可。 (除非两台机器之间的装配版本不同,否则您可能需要自己找到该类型。)