使用IOC

时间:2017-04-27 13:20:14

标签: c# .net generics inversion-of-control solid-principles

我有一个要求,给定一个未知类型的对象,我需要返回一个描述该对象的用户友好字符串。我意识到我可以覆盖ToString方法,但我不能保证这些对象都是我的。

因此,鉴于以下类别:

public class Foo
{
    public string Title { get { return "Foo title"; } }
}
public class Bar
{
    public string Name { get { return "Foo name"; } }
}

我希望能够输出Foo对象的title属性值和Bar对象的Name属性值。

为了实现这一目标,我创建了这个界面:

public interface ITypeStringFormattingService<in T>
{
    string FormatValue(T value);
}

这节课:

public class FooStringFormattingService : ITypeStringFormattingService<Foo>
{
    public string FormatValue(Foo value)
    {
        return value.Title;
    }
}

我可以这样做:

ITypeStringFormattingService<Foo> formatter = new FooStringFormattingService();
string s = formatter.FormatValue(new Foo()); // Will output "Foo title"

我的问题是,因为直到运行时我才知道对象的类型,如何在给定某个对象的情况下获得正确的通用类型TypeStringFormattingService?

我的(简化)要求是这样的:

object unknownObject = ExternalObject.GetRandomObject();
string friendlyString = new FormattingService().FormatValue(unknownObject); // Output expected string

我通常会通过将一系列服务(使用castle windsor)注入工厂/策略类,然后通过某种方法过滤它们来找到我需要的服务来解决这个问题。但是,由于这是一个逆变通用(参见&#34; in&#34;关键字),我不认为这是可能的吗?我是否遗漏了某些内容,或者我的课程应该采用不同的方式来处理我所处理的情况?

到目前为止,我只能通过使用铸造(感觉不正确),服务定位器或新建工厂中的每项服务来解决这个问题。利用IOC)。

示例小提琴:https://dotnetfiddle.net/nxwaJR 任何代码都可以更改,除了&#34; ExternalObject&#34;最理想的是对象类型。

1 个答案:

答案 0 :(得分:0)

下面的代码说明了如何实现这一目标。它包含:

  • 要格式化Foo和Bar。
  • 通用接口ITypeStringFormattingService&lt;&gt;实施格式化程序的定义。
  • 用于格式化Foo和Bar对象的接口的两个实现。
  • (这里开始有趣的部分)通用格式化程序聚合其他格式化程序并在一个地方做一些样板文件。为了运行它需要以某种方式将所有其他格式化程序注入其中。我不熟悉Castle Windsor DI容器(实际上我是第一次看到它),所以也许我错过了什么或做错了什么,但我没有找到一种更好的方法在10分钟内完成它而不是注射DI容器并通过参数类型动态解析它们。但是,这不是答案的重点。
  • Castle.Windsor DI容器的容器访问器。对我来说,实现它有点奇怪,但我还没有找到在服务中注入DI容器的其他方法。 Autofac提供了一种内置的方法。

如果有人知道如何更好地注册这些类型 - 欢迎您留下您的意见。正如我所说,我对温莎城堡不熟悉。

主要部分非常简单。当我们在通用格式化程序上调用FormatValue()时,它首先构建应该格式化这种特定类型对象的封闭泛型类型的接口。然后,我们尝试从DI容器中解析此类型。在解析了正确的格式化程序后,我们调用了FormatValue()方法。关于它的特殊之处在于使用“dynamic”关键字作为变量保持格式化程序并将其作为对象的强制转换。这样做是因为我们不能为formatter指定任何其他类型。我们只会在运行时知道它的真实类型,因为它是通用的,我们不能将它转换为基于相同泛型的任何其他类型。使用“动态”,我们只让DLR使用特定类型的参数确定调用特定格式化程序的正确方法。动态有自己的与反射有关的开销,但这也不是答案的重点,可以进行优化。

using System;
using Castle.MicroKernel.Registration;
using Castle.Windsor;

namespace ConsoleApp1
{
    public class Foo
    {
        public string Title => "Foo title";
    }
    public class Bar
    {
        public string Name => "Foo name";
    }

    public interface ITypeStringFormattingService<in T>
    {
        string FormatValue(T value);
    }

    public class FooStringFormattingService : ITypeStringFormattingService<Foo>
    {
        public string FormatValue(Foo value)
        {
            return value.Title;
        }
    }

    public class BarStringFormattingService : ITypeStringFormattingService<Bar>
    {
        public string FormatValue(Bar value)
        {
            return value.Name;
        }
    }

    public class UniversalFormatter : ITypeStringFormattingService<object>
    {
        private readonly IWindsorContainer _container;

        public UniversalFormatter(IContainerAccessor container)
        {
            _container = container.Container;
        }
        public string FormatValue(object value)
        {
            dynamic formatter = _container.Resolve(typeof(ITypeStringFormattingService<>).MakeGenericType(value.GetType()));
            return formatter.FormatValue((dynamic) value);
        }
    }

    public class ContainerAccessor : IContainerAccessor
    {
        public ContainerAccessor(IWindsorContainer container)
        {
            Container = container;
        }

        public IWindsorContainer Container { get; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var container = new WindsorContainer();

            container.Register(Component.For<IContainerAccessor>().Instance(new ContainerAccessor(container)));
            container.Register(Component.For(typeof(ITypeStringFormattingService<Foo>)).ImplementedBy<FooStringFormattingService>());
            container.Register(Component.For(typeof(ITypeStringFormattingService<Bar>)).ImplementedBy<BarStringFormattingService>());
            container.Register(Component.For(typeof(ITypeStringFormattingService<object>)).ImplementedBy<UniversalFormatter>());

            // Resolving

            var formatter = container.Resolve<ITypeStringFormattingService<object>>();

            object obj = new Bar();
            Console.WriteLine(formatter.FormatValue(obj));

            obj = new Foo();
            Console.WriteLine(formatter.FormatValue(obj));
        }
    }
}