为多个接口返回同一实例

37
我正在使用以下代码注册组件:
StandardKernel kernel = new StandardKernel();

string currentDirectory = Path.GetDirectoryName(GetType().Assembly.Location)
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    if (!Path.GetDirectoryName(assembly.Location).Equals(currentDirectory)) 
        continue;

    foreach (var type in assembly.GetTypes())
    {
        if (!type.IsComponent()) 
            continue;

        foreach (var @interface in type.GetInterfaces())
        kernel.Bind(@interface).To(type).InSingletonScope();
    }
}

我有一个类实现了两个接口:

class StandardConsole : IStartable, IConsumer<ConsoleCommand>

如果我解析IStartable,我会得到一个实例,如果我解析IConsumer<ConsoleCommand>,我会得到另一个实例。

如何获取这两个接口的相同实例?


1
这个问题应该分成两个,一个是关于Ninject的,另一个是关于autofac的。 - Jeff Walker Code Ranger
@JeffWalkerCodeRanger:你给了-1分? :) - jgauffin
1
在Ninject方面有3或4个重复项,请参见https://dev59.com/Y07Sa4cB1Zd3GeqP6cMY。基于此,已删除ninject标签。建议从问题中删除ninject方面,因为这会导致无法回答的问题。 - Ruben Bartelink
5个回答

79
builder.RegisterType<StandardConsole>()
   .As<IStartable>()
   .As<IConsumer<ConsoleCommand>>()
   .SingleInstance();

Autofac是一个广泛使用的特性 - 如果有问题,那么bug肯定在某个地方 :)

Hth Nick

编辑 看起来,你需要使用接收IEnumerable<Type>()参数的As()重载方法 - 可以使用IntelliSense查看所有的As()重载方法,其中一些应该适合你的情况。正如另一个评论者指出的那样,你需要更新问题并提供所有信息。


不,我不想使用IEnumerable<Type>进行注册。这是autofac的内置功能。而且,由于我正在使用反射来注册所有组件,所以我无法使用泛型参数。 - jgauffin
2
我知道你说的功能(我写了它;)) - 我建议的那个不同。如果你有一个要暴露的接口列表(i1, i2, i3..),你可以把整个列表传递给单个As()调用。你也可以看一下RegisterAssemblyTypes(myAsm).AsImplementedInterfaces(),它可以扩展到做你的文章所提出的事情http://code.google.com/p/autofac/wiki/Scanning - 如果你喜欢,欢迎通过Autofac讨论论坛进行讨论。 - Nicholas Blumhardt
抱歉 =)我已经厌倦了那些没有使用autofac的人的回答。我已经在下面更新了我的答案,其中包含来自扫描文章的代码。谢谢! - jgauffin

3

根据Nicholas的建议更新:

以下是在autofac中的实现方法。

    private void BuildComponents(ContainerBuilder builder)
    {
        string currentDirectory = Path.GetDirectoryName(GetType().Assembly.Location);
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            if (!Path.GetDirectoryName(assembly.Location).Equals(currentDirectory))
                continue;

            builder.RegisterAssemblyTypes(assembly)
                .Where(t => t.IsComponent())
                .AsImplementedInterfaces()
                .SingleInstance();
        }
    }

    public static bool IsComponent(this Type value)
    {
        return value.GetType().GetCustomAttributes(typeof (ComponentAttribute), true).Length > 0;
    }

2

我知道这是一个老帖子,但是这里是Ninject的解决方案。

kernel.Bind<StandardConsole>().ToSelf().InSingletonScope();
kernel.Bind<IStartable>().ToMethod(ctx => ctx.Kernel.Get<StandardConsole>());
kernel.Bind<IConsumer<ConsoleCommand>>().ToMethod(ctx => ctx.Kernel.Get<StandardConsole>());

@Jeff:请查看http://www.planetgeek.ch/2011/12/30/new-features-and-changes-of-ninject-3-0/。 - Ruben Bartelink

0

我这么说纯属猜测,因为我不了解Autofac。

如果你添加:

build.RegisterType<StandardConsole>.As(StandardConsole).SingleInstance()

那么它不应该将IStartable解析为StandardConsole,然后将StandardConsole解析为StandardConsole的单例实例吗?IConsumer也是如此。

编辑:从您的博客日志中记录,您能否更改以下内容:

assemblies.Each(assembly => assembly.FindComponents((i, c) => builder.RegisterType(c).As(i).SingleInstance()));

assemblies.Each(assembly => assembly.FindComponents((i, c) => {
    builder.RegisterType(c).As(i).SingleInstance();
    builder.RegisterType(c).As(c).SingleInstance();
}));

请查看我对Dave Thieben的评论。 - jgauffin

0

我不熟悉Autofac,但你应该能够注册一个类型的lambda表达式,该表达式返回另一个类型的解析。

类似这样:

builder.Register<IStartable>().As<StandardConsole>().Singleton();
builder.Register<IConsumer<ConsoleCommand>>().As( x => builder.Resolve<IStartable>() );

1
不行。我会扫描应用程序目录中的所有程序集,查找带有ComponentAttribute的类,并将它们与实现的所有接口一起注册。这使得你所建议的事情相当困难。http://blog.gauffin.org/2010/07/simplified-autofac-registrations/ - jgauffin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接