为了账号安全,请及时绑定邮箱和手机立即绑定

在 Autofac 中使用多态性时解析具体类型

在 Autofac 中使用多态性时解析具体类型

C#
函数式编程 2022-01-15 19:40:03
考虑以下代码:public interface IFileBackup{    Task Backup(byte[] file);}public class BackUpMechanismA : IFileBackup{    //Implementation    public async Task Backup(byte[] file)    {        //Attempts to backup using mechanism A    }}public class BackUpMechanismB : IFileBackup{    //Implementation    public async Task Backup(byte[] file)    {        //Attempts to backup using mechanism B    }}然后调用类如下所示:public class Caller{    private readonly IFileBackup _backupA;    private readonly IFileBackup _backupB;    public Caller(IFileBackup backupA, IFileBackup backupB)    {        _backupA = backupA;        _backupB = backupB;    }     public async Task BackupFile(byte[] file)     {         try         {             await _backupA.Backup(file);         }         catch(SomeException)         {             await _backupB.Backup(file);         }     }}所以我在这里尝试做的是使用多态性。所以两者都BackupMechanismA以自己的方式BackupMechanismB实现该方法。Backup在调用者中,我想尝试第一种机制,如果这不起作用,我们捕获异常并尝试第二种方法。我无法使用Autofac. 我尝试过:builder.RegisterType<BackupMechanismA>().As<IFileBackup>().AsSelf(); builder.RegisterType<BackupMechanismB>().As<IFileBackUp>().AsSelf();但这不起作用,因为我仍然需要告诉调用者要解析哪种类型。我如何在来电者中做到这一点?另外,我怀疑这种设计是否真的是正确的设计。在此设计之前,我只有一个具有两种不同方法的类,一种用于机制 A,一种用于机制 B,然后调用者将在 try catch 中调用不同的方法。所以我想重构它,因为这个类变得非常大,我想把这两种不同的机制分离到它们自己的类中。那么,我可以使用 Autofac 解决这个问题吗?对于这种情况,它是正确的设计吗?
查看完整描述

3 回答

?
绝地无双

TA贡献1946条经验 获得超4个赞

同意 Jogge 的观点,迭代IFileBackups 会是一个更好的选择,但是为每种类型创建一个接口是不行的。相反,您可以添加一个提供IEnumerable<IFileBackup>(聚合)的类。例如:


public class BackupBundle : IEnumerable<IFileBackup>

{

    private readonly List<IFileBackup> _backups = new List<IFileBackup>();


    // default constructor creates default implementations

    public BackupBundle()

        : this(new List<IFileBackup> {new BackUpMechanismA(), new BackUpMechanismB()}) {}


    // allow users to add custom backups

    public BackupBundle(IEnumerable<IFileBackup> backups)

    {

        foreach (var backup in backups)

            Add(backup);

    }


    public void Add(IFileBackup backup)

    {

        if (backup == null) throw new ArgumentNullException(nameof(backup));

        _backups.Add(backup);

    }


    public IEnumerator<IFileBackup> GetEnumerator()

    {

        foreach (var backup in _backups)

            yield return backup;

    }


    IEnumerator IEnumerable.GetEnumerator()

    {

        return GetEnumerator();

    }

}


public class Caller

{

    private readonly IEnumerable<IFileBackup> _backups;


    public Caller(IEnumerable<IFileBackup> backups)

    {

        _backups = backups ?? throw new ArgumentNullException(nameof(backups));

    }


    public async Task BackupFile(byte[] file)

    {

        foreach (var b in _backups)

        {

            try

            {

                await b.Backup(file);

                break;

            }

            catch (Exception e) { }

        }

    }

}

注册可按以下方式进行:


builder.RegisterInstance(new BackupBundle()).As<IEnumerable<IFileBackup>>();

builder.RegisterType<Caller>();

它允许您按类名解析:


var caller = scope.Resolve<Caller>();

如您所见,BackupBundle具有BackUpMechanismA和的依赖关系BackUpMechanismB。您可以通过引入另一层抽象来摆脱它,但我不想这样做。我主要关心的是使Caller更健壮。您可能想要引入重试逻辑、超时等。


查看完整回答
反对 回复 2022-01-15
?
慕桂英3389331

TA贡献2036条经验 获得超8个赞

为了使您的设计工作,您可以尝试下一种方法:


static void Main(string[] args)

{

    var builder = new ContainerBuilder();

    builder.RegisterType<BackUpMechanismA>().Keyed<IFileBackup>("A");

    builder.RegisterType<BackUpMechanismB>().Keyed<IFileBackup>("B");

    builder.RegisterType<Caller>()

            .WithParameter((p, ctx) => p.Position == 0, (p, ctx) => ctx.ResolveKeyed<IFileBackup>("A"))

            .WithParameter((p, ctx) => p.Position == 1, (p, ctx) => ctx.ResolveKeyed<IFileBackup>("B"));

    IContainer container = builder.Build();


    var caller = container.Resolve<Caller>();


    Console.ReadKey();

}

但是在我看来,您可能在这里不需要这样的多态性。实现这样的东西会更加明显和描述性:


public async Task BackupFile(byte[] file)

{

    try

    {

        await BackUpToAmazonS3(file);

    }

    catch (AmazonS3LoadingException)

    {

        await BackUpToLocalDisk(file);

    }

}

在这个例子中,很明显发生了什么。在那里BackUpToAmazonS3你可以使用一些注入AmazonS3FileBackUp和BackUpToLocalDisk使用中的LocalDiskFileBackUp东西。关键是当您不打算更改实现时,您不需要多态性。在您的上下文中应该清楚吗?您尝试将备份放入某个远程存储,然后,如果失败,则放入本地磁盘。您无需在此处隐藏含义。这是你的逻辑,应该很清楚,当你阅读代码时,我想。希望能帮助到你。


查看完整回答
反对 回复 2022-01-15
?
红糖糍粑

TA贡献1815条经验 获得超6个赞

尝试使用名称注册,然后使用名称解析:


builder.RegisterType<BackupMechanismA>().Named<IFileBackup>("BackUpMechanismA");

builder.RegisterType<BackupMechanismB>().Named<IFileBackUp>("BackUpMechanismB");


_backupA = container.ResolveNamed<IFileBackUp> 

("BackUpMechanismA"); 

_backupB = container.ResolveNamed<IFileBackUp> 

("BackUpMechanismB");

在运行时解析实例,而不是通过构造函数注入。这将让您根据需要解析为相应的类型。让我知道这个是否奏效。


查看完整回答
反对 回复 2022-01-15
  • 3 回答
  • 0 关注
  • 178 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信