【问题标题】:Resolve IEnumerable of a generic in constructor with Autofac使用 Autofac 解析构造函数中泛型的 IEnumerable
【发布时间】:2016-08-15 01:12:33
【问题描述】:

所以我有一个抽象工厂类,想法是在这个工厂的构造函数中,我将从 autofac 传递我感兴趣的所有注册服务的IEnumerable。这个工厂里有个方法传一个参数,代码会循环遍历IEnumerable,返回匹配的服务。像馅饼一样简单,抽象工厂模式。

问题是,我最近在工厂返回的接口中引入了一个泛型类型,我不确定如何编写构造函数,所以 autofac 知道仍然返回所有注册的服务,并从本质上忽略泛型。

一些代码:

我的界面:

public interface IRunnerServices<in T>
{
    void Create(T entity);
}

接口的一些实现

public class RunnerServiceA : IRunnerServices<A>
{
    public void Create(A entity);
}

public class RunnerServiceB : IRunnerServices<B>
{
    public void Create(B entity);
}

public class RunnerServiceC : IRunnerServices<C>
{
    public void Create(C entity);
}

// Many, many more...

在我的作文根目录中,我像这样注册这些服务

builder.RegisterType<RunnerServicesA>().As<IRunnerServices<A>>();
builder.RegisterType<RunnerServicesB>().As<IRunnerServices<B>>();
builder.RegisterType<RunnerServicesC>().As<IRunnerServices<C>>();

你知道,A、B、C 都继承自同一个基类

public class A : LetterBase
{ }

public class B : LetterBase
{ }

public class C : LetterBase
{ }

现在,我的抽象工厂

public class RunnerServicesFactory
{
    private readonly IEnumerable<IRunnerServices<????>> _candidates;
    public RunnerServicesFactory(IEnumerable<IRunnerServices<????>> candidates)
    {
        _candidates = candidates;
    }

    public IRunnerServices<????> Create(int discriminator)
    {
        return _candidates.First(c => c.Discriminator == discriminator);
    }
}

问题在于我在上面的构造函数和其他声明中提供了什么????让 autofac 知道它应该传入这个泛型的所有注册服务?

我已经尝试过了,但没有运气,因为 autofac 什么都没通过

IEnumerable<IRunnerServices<LetterBase>>

【问题讨论】:

  • 你想要的是同时使用协变和逆变,这是不可能的。你需要逆变吗?

标签: c# ienumerable autofac abstract-factory


【解决方案1】:

为了将您的RunnerServiceA 实现转换为IRunnerService&lt;LetterBase&gt;,您需要使IRunnerService&lt;T&gt; 与out 通用关键字修饰符协变,这是不可能的,因为您有一种方法可以接受T 参数。

.net 不允许您将 RunnerServiceA 转换为 IRunnerService&lt;LetterBase&gt;,您可以尝试执行以下简单代码:

var runnerService = (IRunnerServices<LetterBase>)new RunnerServiceA();
// Throws an InvalidCastException 

一种可能的解决方案是像这样使IRunnerService&lt;T&gt; 协变:

public interface IRunnerServices<out T>
    where T : LetterBase
{
    void Create(LetterBase entity);
}

通过这样做,您将能够将RunnerServiceA 转换为IRunnerService&lt;LetterBase&gt; 并执行以下代码:

IEnumerable<IRunnerServices<LetterBase>> runners = new IRunnerServices<LetterBase>[] {
                                                        new RunnerServiceA(),
                                                        new RunnerServiceB()
                                                    };

如果RunnerService注册为IRunnerServices&lt;LetterBase&gt;Autofac将能够解析IEnumerable&lt;IRunnerServices&lt;LetterBase&gt;&gt;

顺便说一下你在做什么让我想想IIndex&lt;TKey, T&gt;

public class RunnerServicesFactory
{
    private readonly IIndex<Char, IRunnerServices<LetterBase>> _candidates;
    public RunnerServicesFactory(IIndex<Char, IRunnerServices<LetterBase>> candidates)
    {
        _candidates = candidates;
    }

    public IRunnerServices<LetterBase> Create(Char discriminator)
    {
        return _candidates[discriminator];
    }
}

注册会是这样的:

builder.RegisterType<RunnerServiceA>().Keyed<IRunnerServices<LetterBase>>('A');
builder.RegisterType<RunnerServiceB>().Keyed<IRunnerServices<LetterBase>>('B');
builder.RegisterType<RunnerServiceC>().Keyed<IRunnerServices<LetterBase>>('C');

【讨论】:

  • 我不完全理解您的协变解决方案,您是否输入错误。您声明了 IRunnerServices,但没有任何地方有 T 的输出类型。
  • @raterus out 修饰符仅允许 .net 将 RunnerServiceA 转换为 IRunnerService&lt;LetterBase&gt;
猜你喜欢
  • 2019-02-28
  • 2016-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-06
  • 1970-01-01
  • 2017-05-11
  • 1970-01-01
相关资源
最近更新 更多