【问题标题】:Unity Configuration With Generics使用泛型的 Unity 配置
【发布时间】:2023-03-03 14:00:01
【问题描述】:

我目前正在工作中设计一个新的消息翻译系统,有以下关于 DI 和 Unity 的问题。

我有如下界面:

public interface ITranslate<TInput, TOutput>
{
    TOutput TranslateMessage(TInput message);
}

通过下面的具体实现(其中 InternalMessage 是我开发的自定义类)

public class TestTranslate : ITranslate<byte[], InternalMessage>
{
    InternalMessage Translate(byte[] message)
    {
        // Do the translation here and return the result....
    }
}

但是,我想使用统一通过构造函数将翻译器实例注入到我的翻译器服务中。在服务内的方法中使用。

public class TranslatorService
{    
    private readonly ITranslator translator;
    public TranslatorService(ITranslate translator)
    {
        this.translator = translator;
    }

    public byte[] DoTranslate(string message)
    {
        return translator.TranslateMessage(message);
    }
}

但是我有两个问题:

1) 是否可以在构造函数中包含 ITranslate 而无需指定翻译器将处理的类型(我试图保留服务 和 Translator 尽可能通用,所以如果需要另一个翻译,我只需要换掉 Translator 接口的具体实现。

2) 如果这是可能的,我将如何做到这一点,然后我将在我的统一配置中拥有什么来做到这一点。注意我正在使用 XML 配置(不是我的选择)来 配置我的依赖等。

提前致谢

斯图尔特

【问题讨论】:

  • 您的“在服务中的方法中使用。”显示了非通用接口的用法,该接口在帖子的其他任何地方都没有提到...可能您只是错过了一些声明...
  • 所以一方面你想定义一个通用接口,但另一方面你又不希望它在使用/注入到 TranslatorService 时是通用的?
  • 我之所以有上述构造函数是我不确定是否可以这样做,服务代码只是作为示例。我正在尝试编写一个通用的翻译器类,可以在调用者认为合适的情况下实现,但如果可能的话,我试图避免的主要事情是让翻译器的调用者必须指定类型,理想情况下这将由统一控制容器(再次仅在可能的情况下)。
  • 您能展示一下 TranslatorService 的客户端代码是如何编写的吗?例如,当这些客户不/不应该知道消息的确切类型以及它们是如何翻译时,他们期望的输入和输出类型是什么?

标签: c# generics dependency-injection unity-container


【解决方案1】:

经过一番思考,我假设您想要执行以下操作:

  1. 在某些时候,不同类型的消息进入并且必须
    不知何故翻译成其他类型。
  2. 接收这些消息的组件/客户端现在并且应该不知道应该如何翻译不同类型的消息。
  3. 因此,您希望实现一个通用的 TranslatorService 类,它接受任何消息并根据输入消息神奇地将其转换为正确的类型。
  4. 对于每个特定的翻译,您希望定义一个特定的通用翻译器类,它会自动实例化并由 TranslatorService 类使用,最好使用某种依赖注入。

如果我的假设是正确的,我会建议以下设计。

首先,定义一个必须由每个专门的 Translator-class 实现的通用接口。只使输入类型通用:

interface IMessageTranslator<TMessage>
{
    object Translate(TMessage message);
}

然后创建 TranslatorService 以便客户端可以推送任意消息并将结果返回给他们,当且仅当该消息的 Translator 存在时:

class TranslatorService
{
    object Translate(object message)
    {

    }
}

现在TranslatorService 必须根据消息的类型实例化正确的IMessageTranslator&lt;T&gt;您可以通过让TranslatorService 类封装一个包含所有IMessageTranslator&lt;T&gt; 的容器来使用Unity 完成此操作。您已经实现的类型,然后根据消息类型简单地实例化一个:

class TranslatorService
{
    private readonly IUnityContainer _container;

    public TranslatorService()
    {
        _container = new UnityContainer();
        _container.RegisterType(typeof(IMessageTranslator<A>), typeof(MessageTranslatorA));
        _container.RegisterType(typeof(IMessageTranslator<B>), typeof(MessageTranslatorB));
        // Etc...
    }

    object Translate(object message)
    {
        if (message == null)
        {
           throw new ArgumentNullException("message");
        }
        var genericTranslatorDefinition = typeof(IMessageTranslator<>);
        var translatorType = genericTranslatorDefinition.MakeGenericType(message.GetType());
        var translator = _container.Resolve(translatorType);
        var translateMethod = translatorType.GetMethod("Translate", new [] { message.GetType() });

        return translateMethod.Invoke(translator, new object[] { message });
    }
}

请注意,对于翻译器是否实际存在/是否已为指定消息注册,以及它找到翻译方法的方式(最好通过 InterfaceMap 获取它),这是一个幼稚的实现,但你得到了想法。

如果您想要更大的灵活性,您可以创建一种机制,通过扫描程序集的 IMessageTranslator 实现程序来自动填充您的容器。这样,您只需添加一个新的实现,就可以开始了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-30
    • 2011-02-07
    • 2017-09-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多