反思实现界面

时间:2013-05-24 07:44:33

标签: c# reflection

这完全是新的。

我想要实现的是,有一个界面,来自外部的人实现它,并在我的程序中加载“实现”并将其绑定到我的界面,所以我有一些关于这个和有什么限制的问题我需要满足。

1-对于实现我的界面的用户,我给他一个包含我的界面的dll,或者只是源代码并且他使用它并添加他的实现代码?

1.1-如果1为真,在我的程序中,我使用哪个界面?我可以使用直接从我的代码加载的接口,或者我强制使用我给用户的同一个DLL的接口吗?

2- Do名称空间需要相同吗?例如,我这边的接口在命名空间Server.Interface中,但是我发送的dll中的接口只是命名空间接口。

我尝试了两种方法来验证程序集是否实现了我的接口:

都在循环中:

foreach (Type t in plugin.GetTypes())
 {

方法1

if (typeof(INovedades).IsAssignableFrom(t))
                {
                    i = (INovedades)Activator.CreateInstance(t);
                    break;
                }

方法2

Type typeInterface = t.GetInterface("CapaDatos.ServiciosExternos.INovedades", true);

                if (typeInterface != null)
                {
                    i = (INovedades)Activator.CreateInstance(t);
                    break;
                }

方法1始终为假,这意味着它永远不会验证。

方法2,找到匹配项,但是在调用CreateInstance时,它会给出一个关于无法创建实例的异常。

我应该知道什么?

3 个答案:

答案 0 :(得分:3)

典型的方法是创建一个项目,其中放置所有合同(合同是接口)。应该没有业务逻辑。 然后由您自己的应用程序(实现逻辑)使用它,这是您可以提供给其他人实现它的程序集。 命名空间应该与使用的dll相同。

您可能希望做的是创建某种插件系统 Writing C# Plugin System

您可以使用您描述的两种方法,我个人更喜欢方法1。

需要更多代码来解释这一点。 首先考虑动态创建对象时:您需要一个没有任何参数的简单构造函数,否则激活器将无法创建实例。

我认为方法1失败,因为插件和消费者之间没有共享契约

答案 1 :(得分:0)

关于您的第一个问题(1 - 是将接口分发为DLL还是通过源代码分发),您必须分发DLL。否则,.NET Runtime将不会将接口视为相同,因为接口的用户和实现它的用户将具有两个版本的DLL。 通常,您将为不包含程序的接口创建单独的DLL。

关于问题1.1,您必须从DLL引用单独的接口DLL,并且接口的用户/实现者也必须引用它。 由于您都使用相同的DLL,因此名称空间将是相同的。

为了创建实例(关于方法1和方法2),您必须创建类的实例,而不是接口的实例。所以你需要从某个地方知道类的类型。 Getting all types that implement an interface中描述了一种方法。 但您也可以从字符串创建它,如Create class instance from string中所述。

答案 2 :(得分:0)

根据我在DLL-s(又名插件)中实现接口的经验,我使用过:

namespace TransportInterface
{
    public interface Transport
    {
        string Name { get; }
        // etc
    }
}

将其编译成DLL文件TransportInterface.dll

之后,将此DLL作为参考添加到 1)应用程序本身的项目 2)使用此接口创建的任何插件的项目

实现此接口的插件:

namespace IRCTransport
{
    public class IRCTransport : Transport
    {
        // here's the implementation
    }
}

这里是从应用程序的项目加载插件(来自你的帖子的方法#2)

using TransportInterface;

// ...

private void LoadTransportPlugins()
{
    string folder = System.AppDomain.CurrentDomain.BaseDirectory + "\\Transports";

    string[] files = Directory.GetFiles(folder, "*.dll");

    foreach (string file in files)
        try
        {
            Assembly assembly = Assembly.LoadFile(file);

            foreach (Type type in assembly.GetTypes())
            {
                Type iface = type.GetInterface("TransportInterface.Transport");

                if (iface != null)
                {
                    try
                    {
                        Transport plugin = (Transport)Activator.CreateInstance(type);

                        // executing this code means transport creation succeded
                    }
                    catch (Exception ex)
                    {
                        _System(ex.InnerException.Message + '\n', Color.Red);
                    }
                    AnyPlugins = true;
                }
            }
        }
        catch { };
}

这是几年前在.NET 3.5上写的,但我想从来没有任何改变。

所有三个名称空间: 1)'传输'界面 2)实现接口的插件 3)使用插件的应用程序

是不同的,我怀疑它们实际上需要是相同的。

如果你只是得到一个无法创建实例的例外,我想你需要验证插件构造不会触发任何异常。您需要更深入地使用“InnerException”调试此案例(可能甚至更多一次),这应该显示您的实际错误是什么,Activator的异常在这里几乎没用。