C# 如何创建返回不同接口类型和抽象类的泛型工厂

C# 如何创建返回不同接口类型和抽象类的泛型工厂,c#,C#,我有一个抽象基类,它包含大量共享代码和配置属性。我已经将大部分共享代码分解为逻辑接口,这些接口也由基类实现。 每个客户都有一系列基类的实现 我目前为每个接口都有一个工厂。每个工厂都有相同的开关语句。我想创建一个通用工厂,它将根据类的声明方式返回不同的功能子集 我的基类: 公共抽象类BaseParser:IDatabaseCreation、IBulkImport、IAnalyticFeature //…许多配置字段、方法和抽象方法 客户类别: 类apaser:BaseParser { 专用int

我有一个抽象基类,它包含大量共享代码和配置属性。我已经将大部分共享代码分解为逻辑接口,这些接口也由基类实现。 每个客户都有一系列基类的实现

我目前为每个接口都有一个工厂。每个工厂都有相同的开关语句。我想创建一个通用工厂,它将根据类的声明方式返回不同的功能子集

我的基类:

公共抽象类BaseParser:IDatabaseCreation、IBulkImport、IAnalyticFeature
//…许多配置字段、方法和抽象方法
客户类别:

类apaser:BaseParser { 专用int组件指示或列; 公共分离机(ILogger日志):基本(日志){ //…配置值和抽象方法实现 当前基础工厂:

class BulkImportFactory
{
    public IBulkImport CreateDatabaseCreationObject(string key, ILogger log)
    {
        switch (key)
        {
            case "A":
                return new AParser(log);
            case "B":
                return new BParser(log);
            case "C":
                return new CParser(log);
            default:
                throw new NotImplementedException("Not Recognized or Not Registered in Factory");
        }
    }
}
类基工厂
{
公共BaseParser CreateParser(字符串键、ILogger日志)
{
开关(钥匙)
{
案例“A”:
返回新的服务器(日志);
案例“B”:
返回新的BParser(日志);
案例“C”:
返回新的CParser(日志);
违约:
抛出新的NotImplementedException(“未被认可或未在工厂注册”);
}
}
}
样本接口工厂:

class BulkImportFactory
{
    public IBulkImport CreateDatabaseCreationObject(string key, ILogger log)
    {
        switch (key)
        {
            case "A":
                return new AParser(log);
            case "B":
                return new BParser(log);
            case "C":
                return new CParser(log);
            default:
                throw new NotImplementedException("Not Recognized or Not Registered in Factory");
        }
    }
}
这是我尝试在一个不起作用的通用工厂:

公共类GenericFactory
{
公共T CreateVariableInterfaceObject(字符串键,ILogger日志),其中T:BaseParser
{
开关(钥匙)
{
案例“A”:
返回新的服务器(日志);
案例“B”:
返回新的BParser(日志);
案例“C”:
返回新的CParser(日志);
违约:
抛出新的NotImplementedException(“未识别或未在GenericFactory中注册”);
}
}
}
正如你所看到的,工厂中的逻辑是相同的和重复的。但是我不能让一个通用解析器工作。不确定我缺少什么语法

我想做的是让所有这些都成为一个工厂:

ParserFactory ParserFactory=new ParserFactory();
BaseParser=parserFactory.CreateParser(queueMessage.key,log);
BulkImportFactory BulkImportFactory=new BulkImportFactory();
IBulkImport bulkImporter=bulkImportFactory.CreateDatabaseCreationObject(键,日志);
AnalyticFeatureFactory parserFactory=新的AnalyticFeatureFactory();
IAnalyticFeatureParser=parserFactory.CreateAnalyticFeatureObject(键,日志);

让我看看是否可以澄清您的意图

您有一组解析器(apaser、BParser、CParser)具有
BaseParser
某些函数的特定实现。您试图做的是在运行时为
AParser
BParser
等的特定实例提供一些特殊功能。因此,假设您需要
AParser
但默认的
AParser
实现不支持
>ParseFoo()
但在运行时,您希望赋予它
ParseFoo()
功能而不提前定义它吗


如果是这样的话,我想你将不得不考虑一个可能与你的工厂一起。与装饰器,你将实现在他们自己的类中的新功能的功能,然后你的工厂将返回装饰器,简单地包装<代码> BaseParser < /代码>混凝土类。这是你的需要吗

sealed class GenericFactory<TKey, TOption, TObject>
{
    readonly IReadOnlyDictionary<TKey, Func<TKey, TOption, TObject>> _factories;

    public GenericFactory(
        IReadOnlyDictionary<TKey, Func<TKey, TOption, TObject>> factories)
    {
        _factories = factories;
    }

    public bool TryCreate(TKey key, TOption option, out TObject @object)
    {
        @object = default;
        if (!_factories.TryGetValue(key, out var factory))
            return false; // Cannot create; unknown key
        @object = factory(key, option);
        return true;
    }
}

static class GenericFactoryExtensions
{
    public static TObject CreateOrFail<TKey, TOption, TObject>(
        this GenericFactory<TKey, TOption, TObject> factory,
        TKey key,
        TOption option)
    {
        if (!factory.TryCreate(key, option, out var @object))
            throw new NotImplementedException($"Not Recognized or Not Registered in {nameof(GenericFactory<TKey, TOption, TObject>)}");
        return @object;
    }
}

void SimpleUseFactory()
{
    var baseParserFactory = new GenericFactory<string, ILogger, BaseParser>(new Dictionary<string, Func<string, ILogger, BaseParser>>
        {
            ["A"] = (key, logger) => new AParser(logger),
            ["B"] = (key, logger) => new BParser(logger)
        });

    var parser = baseParserFactory.CreateOrFail("A", logger);
    parser.DoStuff();
}

class Factories
{
    public Func<string, ILogger, BaseParser> BaseParserFactory { get; }
    public Func<string, ILogger, IBulkImport> BulkImportFactory { get; }
    public Func<string, ILogger, SomethingElse> SomethingElseFactory { get; }

    public Factories(
        Func<string, ILogger, BaseParser> baseParserFactory,
        Func<string, ILogger, IBulkImport> bulkImportFactory,
        Func<string, ILogger, SomethingElse> somethingElseFactory)
    {
        BaseParserFactory = baseParserFactory;
        BulkImportFactory = bulkImportFactory;
        SomethingElseFactory = somethingElseFactory;
    }
}

void ComplexUseFactory()
{
    var mappedFactories = new Dictionary<string, Factories>
    {
        ["A"] = new Factories(
            baseParserFactory: (key, logger) => new AParser(logger),
            bulkImportFactory: (key, logger) => new ABulkImport(logger),
            somethingElseFactory: (key, logger) => new ASomethingElse(logger)),
        ["B"] = new Factories(
            baseParserFactory: (key, logger) => new BParser(logger),
            bulkImportFactory: (key, logger) => new BBulkImport(logger),
            somethingElseFactory: (key, logger) => new BSomethingElse(logger))
    };

    var baseParserFactory = new GenericFactory<string, ILogger, BaseParser>(
        mappedFactories.ToDictionary(
            keySelector: kvp => kvp.Key,
            elementSelector: kvp => kvp.Value.BaseParserFactory));
    var bulkImportFactory = new GenericFactory<string, ILogger, IBulkImport>(
        mappedFactories.ToDictionary(
            keySelector: kvp => kvp.Key,
            elementSelector: kvp => kvp.Value.BulkImportFactory));
    var somethingElseFactory = new GenericFactory<string, ILogger, SomethingElse>(
        mappedFactories.ToDictionary(
            keySelector: kvp => kvp.Key,
            elementSelector: kvp => kvp.Value.SomethingElseFactory));

    var parser = baseParserFactory.CreateOrFail("A", logger);
    parser.DoStuff();
}
ModuleB.cs

Register<AParser>().As<BaseParser>();
Register<ABulkImport>().As<IBulkImport>();
Register<BParser>().As<BaseParser>();
Register<BBulkImport>().As<IBulkImport>();
public class CommonThing
{
    readonly BaseParser _parser;
    readonly IBulkImport _bulkImport;

    public CommonThing(
        BaseParser parser,
        IBulkImport bulkImport)
    {
        _parser = parser;
        _bulkImport = bulkImport;
    }

    public void DoFancyStuff(string data)
    {
        var parsed = _parser.Parse(data);
        _bulkImport.Import(parsed);
    }
}
public class Application
{
    readonly CommonThing _commonThing;

    public Application(
        CommonThing commonThing)
    {
        _commonThing = commonThing;
    }

    public void Run()
    {
        var json = "{\"key\":\"value\"}";
        _commonThing.DoFancyStuff(json);
    }
}
public class ModuleNameAttribute : Attribute
{
    public string Name { get; }
    ...
}
[ModuleName("A")]
public class ModuleA
{
    ...
}
[ModuleName("B")]
public class ModuleB
{
    ...
}
单成分根

switch (module)
{
    case "A":
        RegisterModule<ModuleA>();
        break;
    case "B":
        RegisterModule<ModuleB>();
        break;
    default:
        throw new NotImplementedException($"Unexpected module {module}");
}
Register<CommonThing>();
Register<Application>();
var moduleType = GetAllTypesInAppDomain()
    .Select(type => (type, type.GetCustomAttribute<ModuleNameAttribute>()))
    .Where(tuple => tuple.Item2 != null)
    .Where(tuple => tuple.Item2.Name == module)
    .FirstOrDefault();
if (moduleType == null)
    throw new NotImplementedException($"No module has a {nameof(ModuleNameAttribute)} matching the requested module {module}");
RegisterModule(moduleType);

...
Program.cs(或您选择的入口点)

模块a.cs

Register<AParser>().As<BaseParser>();
Register<ABulkImport>().As<IBulkImport>();
Register<BParser>().As<BaseParser>();
Register<BBulkImport>().As<IBulkImport>();
public class CommonThing
{
    readonly BaseParser _parser;
    readonly IBulkImport _bulkImport;

    public CommonThing(
        BaseParser parser,
        IBulkImport bulkImport)
    {
        _parser = parser;
        _bulkImport = bulkImport;
    }

    public void DoFancyStuff(string data)
    {
        var parsed = _parser.Parse(data);
        _bulkImport.Import(parsed);
    }
}
public class Application
{
    readonly CommonThing _commonThing;

    public Application(
        CommonThing commonThing)
    {
        _commonThing = commonThing;
    }

    public void Run()
    {
        var json = "{\"key\":\"value\"}";
        _commonThing.DoFancyStuff(json);
    }
}
public class ModuleNameAttribute : Attribute
{
    public string Name { get; }
    ...
}
[ModuleName("A")]
public class ModuleA
{
    ...
}
[ModuleName("B")]
public class ModuleB
{
    ...
}
ModuleB.cs

Register<AParser>().As<BaseParser>();
Register<ABulkImport>().As<IBulkImport>();
Register<BParser>().As<BaseParser>();
Register<BBulkImport>().As<IBulkImport>();
public class CommonThing
{
    readonly BaseParser _parser;
    readonly IBulkImport _bulkImport;

    public CommonThing(
        BaseParser parser,
        IBulkImport bulkImport)
    {
        _parser = parser;
        _bulkImport = bulkImport;
    }

    public void DoFancyStuff(string data)
    {
        var parsed = _parser.Parse(data);
        _bulkImport.Import(parsed);
    }
}
public class Application
{
    readonly CommonThing _commonThing;

    public Application(
        CommonThing commonThing)
    {
        _commonThing = commonThing;
    }

    public void Run()
    {
        var json = "{\"key\":\"value\"}";
        _commonThing.DoFancyStuff(json);
    }
}
public class ModuleNameAttribute : Attribute
{
    public string Name { get; }
    ...
}
[ModuleName("A")]
public class ModuleA
{
    ...
}
[ModuleName("B")]
public class ModuleB
{
    ...
}
单成分根

switch (module)
{
    case "A":
        RegisterModule<ModuleA>();
        break;
    case "B":
        RegisterModule<ModuleB>();
        break;
    default:
        throw new NotImplementedException($"Unexpected module {module}");
}
Register<CommonThing>();
Register<Application>();
var moduleType = GetAllTypesInAppDomain()
    .Select(type => (type, type.GetCustomAttribute<ModuleNameAttribute>()))
    .Where(tuple => tuple.Item2 != null)
    .Where(tuple => tuple.Item2.Name == module)
    .FirstOrDefault();
if (moduleType == null)
    throw new NotImplementedException($"No module has a {nameof(ModuleNameAttribute)} matching the requested module {module}");
RegisterModule(moduleType);

...
var moduleType=GetAllTypesInAppDomain()
.Select(type=>(type,type.GetCustomAttribute())
.Where(tuple=>tuple.Item2!=null)
.Where(tuple=>tuple.Item2.Name==module)
.FirstOrDefault();
if(moduleType==null)
抛出新的NotImplementedException($“没有模块的{nameof(ModuleNameAttribute)}与请求的模块{module}匹配”);
注册模块(moduleType);
...
请注意,始终使用依赖项注入的好处之一(这意味着注册/解析应用程序本身,就像上面提到的
Program.cs
那样)缺少注册会导致非常早期的运行时异常。这通常不需要某种编译时保证所有正确的东西都在正确的位置


例如,如果
module
被定义为“C”,那么
NotImplementedException
在“singlecompositionroot”中将在应用程序启动时抛出。或者,如果模块C确实存在,但未能注册
IBulkImport
的实现,则IoC容器将在尝试解决
应用程序的
常见问题时抛出运行时异常,再次在应用程序启动时抛出。因此,如果应用程序启动,则您知道l依赖项已解决或可以解决。

似乎您的代码违反了坚实的原则,例如单一用途类。如果您想解决这一问题,而不是解决实现每个接口的基类,则它们可以包含每个接口的属性,例如解析器、批量导入器、分析 但是如果您想用当前的体系结构简单地解决这个问题,您可以执行以下操作

您已经定义了一个基本解析器实现3个接口。从同一工厂获取每个接口

//Note: you probably want to change the name of your factory
ParserFactory parserFactory = new ParserFactory();
BaseParser parser = parserFactory.CreateParser(queueMessage.key,log);
IBulkImport bulkImporter =  parserFactory.CreateParser(queueMessage.key,log);
如果出于某种原因,您需要添加其他非解析器的批量导入器,那么只需创建一个封装逻辑的类,例如

在你的身边