我正在实现一种工厂模式,并在代码审查中找到this neat-looking pattern。
我已经实现了该解决方案,其中包括以下几种变化:
我有一个工厂类,看起来像这样:
public class SearchableServiceFactory<TSearchableLookupService, TOutputDto>
where TOutputDto : IBaseOutputDto
where TSearchableLookupService : ISearchableLookupService<TOutputDto>
{
static readonly Dictionary<string, Func<TSearchableLookupService>> _SearchableLookupServicesRegistry =
new Dictionary<string, Func<TSearchableLookupService>>();
private SearchableServiceFactory() { }
public static TSearchableLookupService Create(string key)
{
if (_SearchableLookupServicesRegistry.TryGetValue(
key, out Func<TSearchableLookupService> searchableServiceConstructor)
)
return searchableServiceConstructor();
throw new NotImplementedException();
}
public static void Register<TDerivedSearchableService>
(
string key,
Func<TSearchableLookupService> searchableServiceConstructor
)
where TDerivedSearchableService : TSearchableLookupService
{
var serviceType = typeof(TDerivedSearchableService);
if (serviceType.IsInterface || serviceType.IsAbstract)
throw new NotImplementedException();
_SearchableLookupServicesRegistry.Add(key, searchableServiceConstructor);
}
那行得通。我从代码中调用它,因此:
...
SearchableServiceFactory<OrgLookupService, OrgOutputDto>.Register<OrgLookupService>
(
nameof(Organization), () => new OrgLookupService(_Context, _OrganizationRepository)
);
...
那行得通。构造函数和一个键一起添加到字典中。然后,我要通过键来检索该构造函数,以获取实例并对其进行处理,例如:
SearchableServiceFactory<ISearchableLookupService<IBaseOutputDto>, IBaseOutputDto>.Create(myKey).DoAThing();
失败,因为字典中不存在这样的值。因为它是静态的,所以类中注册和创建所需实例的方法也是如此。
如果这很重要,我正在使用.NET Core 2.1(这似乎是严格的C#问题)。
答案 0 :(得分:1)
SearchableServiceFactory<OrgLookupService, OrgOutputDto>
与SearchableServiceFactory<ISearchableLookupService<IBaseOutputDto>, IBaseOutputDto>
类型不同,因此,甚至静态属性也不同。
在编译器看来,它们是不同的类型。仅仅因为OrglookupService
是ISearchableLookupService
,并不是每个ISearchableLookupService
都是OrglookupService
。
可能的解决方法是使用SearchableServiceFactory<ISearchableLookupService<IBaseOutputDto>, IBaseOutputDto>
注册对象,但这需要ISearchableLookupService
是协变的。
public interface ISearchableLookupService<out TOutputDto>
where TOutputDto : IBaseOutputDto
{
}
并这样注册:
SearchableServiceFactory<ISearchableLookupService<IBaseOutputDto>, IBaseOutputDto>.Register<OrgLookupService>
(
nameof(Organization), () => new OrgLookupService()
);
答案 1 :(得分:0)
类SearchableServiceFactory<A, B>
与
类不同
SearchableServiceFactory<X, Y>
。因此,您要处理两组不同的静态成员。特别是,您有两个不同的字典_SearchableLookupServicesRegistry
。
您正在其中一个中注册(在SearchableServiceFactory<OrgLookupService, OrgOutputDto>
中)。另一个(在
SearchableServiceFactory<ISearchableLookupService<IBaseOutputDto>, IBaseOutputDto>
)保持为空,但您尝试将其用于构造函数的检索。如果您在throw
的{{1}}语句上设置断点并检查Create
,则会看到其_SearchableLookupServicesRegistry
为Count
。
泛型的问题在于它们似乎提供了一些动态行为,但它们却没有。确定所有泛型类型参数的编译时间。使用泛型的复杂方案通常会变得非常复杂。如果您需要高度动态化,则有时必须放弃全类型安全性。
这是我对服务工厂的建议:
0
请注意,public static class SearchableServiceFactory
{
static readonly Dictionary<string, Func<ISearchableLookupService<IBaseOutputDto>>>
_SearchableLookupServicesRegistry =
new Dictionary<string, Func<ISearchableLookupService<IBaseOutputDto>>>();
public static TSearchableLookupService Create<TSearchableLookupService>(string key)
where TSearchableLookupService : ISearchableLookupService<IBaseOutputDto>
{
if (_SearchableLookupServicesRegistry.TryGetValue(
key,
out Func<ISearchableLookupService<IBaseOutputDto>> searchableServiceConstructor))
{
return (TSearchableLookupService)searchableServiceConstructor();
}
throw new ArgumentException($"Service for \"{key}\" not registered.");
}
public static void Register(
string key,
Func<ISearchableLookupService<IBaseOutputDto>> searchableServiceConstructor)
{
_SearchableLookupServicesRegistry.Add(key, searchableServiceConstructor);
}
}
不是通用的。对于只有一个工厂,因此只有一个静态字典,这是必需的。
它使用具有SearchableServiceFactory
修饰符的修改后的界面。 out
修饰符增加协方差。即,您可以为其提供派生类型;但是,用它修饰的通用类型只能作为返回类型或在out
参数中出现。
out
您可以注册
public interface ISearchableLookupService<out TOutputDto> where TOutputDto : IBaseOutputDto
{
TOutputDto GetOutputDto();
}
并使用
创建SearchableServiceFactory.Register(
nameof(Organization), () => new OrgLookupService(_Context, _OrganizationRepository));
或获得更具体的类型
IBaseOutputDto result = SearchableServiceFactory
.Create<ISearchableLookupService<IBaseOutputDto>>(nameof(Organization))
.GetOutputDto();
但是最后一个示例使字符串键OrgOutputDto result = SearchableServiceFactory
.Create<ISearchableLookupService<OrgOutputDto>>(nameof(Organization))
.GetOutputDto();
变得多余,因为nameof(Organization)
本身的类型可以用作键。 (我需要一些反思才能从OrgOutputDto
中提取出来。)