我有一个要求,给定一个未知类型的对象,我需要返回一个描述该对象的用户友好字符串。我意识到我可以覆盖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;最理想的是对象类型。
答案 0 :(得分:0)
下面的代码说明了如何实现这一目标。它包含:
如果有人知道如何更好地注册这些类型 - 欢迎您留下您的意见。正如我所说,我对温莎城堡不熟悉。
主要部分非常简单。当我们在通用格式化程序上调用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));
}
}
}