TinyIoC - 接口的多个实现

8

我刚开始学习IoC和依赖注入。我计划做一个MonoTouch项目,并想使用TinyIoC,但我想先测试一下。我正在创建一个虚拟信用卡处理控制台应用程序,但我在如何配置TinyIoC方面遇到了麻烦,因为我有多个接口实现。这是我的测试应用程序。

接口

public interface IPaymentProcessor
{
    void ProcessPayment(string cardNumber);
}

接口的两个实现:

VisaPaymentProcessor

public class VisaPaymentProcessor : IPaymentProcessor
{
    public void ProcessPayment(string cardNumber)
    {
        if (cardNumber.Length != 13 && cardNumber.Length != 16)
        {
            new ArgumentException("Card Number isn't the correct length");
        }

        // some code for processing payment
    }
}

AmexPaymentProcessor

public class AmexPaymentProcessor : IPaymentProcessor
{
    public void ProcessPayment(string cardNumber)
    {
        if (cardNumber.Length != 15)
        {
            new ArgumentException("Card Number isn't the correct length");
        }

        // some code for processing the payment
    }
}

很简单的东西。现在我有一个类,它在构造函数中接受接口作为参数....

CreditCardProcessor

public class CreditCardProcessor
{
    public IPaymentProcessor PaymentProcessor { get; set; }

    public CreditCardProcessor(IPaymentProcessor processor)
    {
        this.PaymentProcessor = processor;
    }

    public void ProcessPayment(string creditCardNumber)
    {
        this.PaymentProcessor.ProcessPayment(creditCardNumber);
    }
}

我的控制台应用程序长这样...

class Program
{
    static void Main(string[] args)
    {
        TinyIoCContainer.Current.AutoRegister();

        var creditCardProcessor = TinyIoCContainer.Current.Resolve<CreditCardProcessor>();
        creditCardProcessor.ProcessPayment("1234567890123456"); // 16 digits
    }
}

所以我正在尝试弄清楚如何告诉Resolve将接口的哪个实现传递给构造函数。如果我运行这段代码,我将始终使用VisaPaymentProcessor实现。

那么我该如何让TinyIoC传递AmexPaymentProcessor实现到构造函数中,而不是VisaPaymentProcessor(似乎是默认值)?

3个回答

7

我自己没有使用过TinyIoC,但我猜想你需要:

TinyIoCContainer.Current.Register(typeof(IPaymentProcessor),
                                  typeof(AmexPaymentProcessor));

(如果您想使用美国运通卡。)

还有其他各种Register重载可用,包括一个带有名称参数的选项,当您解析时可能会很有用。这取决于您尝试实现的目标,而问题并没有非常清晰地表达出来。


谢谢。我已经更新了帖子并提出了问题。似乎VisaPaymentProcessor是TinyIoC使用的“默认”实现。我该如何让TinyIoC将AmexPaymentProcessor实现传递给构造函数呢?抱歉没有表述清楚。 - Ryan Alford
@Eclipsed4utoo:好的 - 在这种情况下,我希望我的答案可以涵盖它 :) - Jon Skeet
@JonSkeet 抱歉我理解不够,但这怎么解决原始问题呢?看起来他们想在运行时有条件地解析已注册的依赖项。 - tom redfern
@Tom:我在问题中没有看到任何关于在执行时确定它的内容。 - Jon Skeet
@JonSkeet,他们是不是在问:“那么我该如何告诉Resolve将接口的哪个实现传递给构造函数?” 在这种情况下,构造函数是public CreditCardProcessor(IPaymentProcessor processor) - tom redfern
@Tom:我不这么认为——但是鉴于原帖作者没有回复更多的评论,很难说(而且已经过了很长时间)。原帖作者只是说他们想使用Amex卡,没有提到有时使用Amex卡,有时使用Visa卡。 - Jon Skeet

2
我不太确定您在这里想要实现什么,但如果您有多个接口的实现,并且您想要一个特定的实现,则需要使用名称注册每个实现,或者使用RegisterMultiple(使用类型名称作为名称),然后使用该名称解析并使用NamedParameterOverloads指定您想要的实现。
不过,更像是您可能需要某种ProcessorFactory,或一种外观模式,它依赖于IEnumerable并根据传入的数量提供/充当正确实现的外观。

1
我读到了关于Windsor IoC的一些内容,并且在想TinyIoC是否也是如此... "现在,当您“解析”CreditCardProcessor的实例时,容器会查看构造函数并发现它需要一个IPaymentProcessor。容器会查看该类型是否在容器中注册。如果已经注册,则容器将“解析”该类型,然后实例化CreditCardProcessor。" 如果是这样,那么当有两个实现已经为接口注册时,TinyIoC会如何处理呢?在使用“RegisterMultiple”时,它似乎选择第一个注册的类型。 - Ryan Alford
1
@Eclipsed4utoo,一个接口只有一个“默认”注册(没有名称的注册),如果您依赖于它,则会得到该注册。当您使用AutoRegister时,“默认”注册不是保证的,因此您应该自己设置它,或者注册所有并按名称提取正确的注册。 - Steven Robbins
谢谢Steven。我能找到的唯一指定正确实现的方法是在解析CreditCardProcessor时使用NamedParameterOverloads。然而,这似乎会“硬编码”构造函数的签名,因为我需要NamedParametersOverload来匹配构造函数的签名。如果构造函数发生变化,如果我不在那里也进行更改,NamedParametersOverload将会失败。对我来说,这有点违背了使用IoC的目的。我是不是完全错了? - Ryan Alford

2
在Global.asax或应用程序入口中,可以编写以下类似的代码(针对您的示例进行了修改):
        const string nameTrim = "paymentprocessor"; 
        var type = typeof(IPaymentProcessor);
        AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(s => s.GetTypes())
            .Where(x => type.IsAssignableFrom(x) && x.IsClass).ToList()
            .ForEach(t =>
            {
                var name = t.Name.ToLower();
                if (name.EndsWith(nameTrim))
                    name = name.Substring(0, name.Length - nameTrim.Length);

                TinyIoCContainer.Current.Register(type, t, name);
            });

它会查找所有实现了IPaymentProcessor接口的类,并将它们注册到类名中(如果类名以PaymentProcessor结尾,则为-PaymentProcessor)。

然后,我就可以解决例如“AmexPaymentProcessor”之类的问题。

        IPaymentProcessor handler;
        if (TinyIoCContainer.Current.TryResolve("amex", out handler))
        {
            response = handler.ProcessPayment(cardNumber);
        }

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