猿问

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

考虑以下代码:


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 解决这个问题吗?对于这种情况,它是正确的设计吗?


函数式编程
浏览 270回答 3
3回答

绝地无双

同意 Jogge 的观点,迭代IFileBackups 会是一个更好的选择,但是为每种类型创建一个接口是不行的。相反,您可以添加一个提供IEnumerable<IFileBackup>(聚合)的类。例如:public class BackupBundle : IEnumerable<IFileBackup>{&nbsp; &nbsp; private readonly List<IFileBackup> _backups = new List<IFileBackup>();&nbsp; &nbsp; // default constructor creates default implementations&nbsp; &nbsp; public BackupBundle()&nbsp; &nbsp; &nbsp; &nbsp; : this(new List<IFileBackup> {new BackUpMechanismA(), new BackUpMechanismB()}) {}&nbsp; &nbsp; // allow users to add custom backups&nbsp; &nbsp; public BackupBundle(IEnumerable<IFileBackup> backups)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; foreach (var backup in backups)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Add(backup);&nbsp; &nbsp; }&nbsp; &nbsp; public void Add(IFileBackup backup)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; if (backup == null) throw new ArgumentNullException(nameof(backup));&nbsp; &nbsp; &nbsp; &nbsp; _backups.Add(backup);&nbsp; &nbsp; }&nbsp; &nbsp; public IEnumerator<IFileBackup> GetEnumerator()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; foreach (var backup in _backups)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; yield return backup;&nbsp; &nbsp; }&nbsp; &nbsp; IEnumerator IEnumerable.GetEnumerator()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return GetEnumerator();&nbsp; &nbsp; }}public class Caller{&nbsp; &nbsp; private readonly IEnumerable<IFileBackup> _backups;&nbsp; &nbsp; public Caller(IEnumerable<IFileBackup> backups)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; _backups = backups ?? throw new ArgumentNullException(nameof(backups));&nbsp; &nbsp; }&nbsp; &nbsp; public async Task BackupFile(byte[] file)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; foreach (var b in _backups)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await b.Backup(file);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; catch (Exception e) { }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}注册可按以下方式进行:builder.RegisterInstance(new BackupBundle()).As<IEnumerable<IFileBackup>>();builder.RegisterType<Caller>();它允许您按类名解析:var caller = scope.Resolve<Caller>();如您所见,BackupBundle具有BackUpMechanismA和的依赖关系BackUpMechanismB。您可以通过引入另一层抽象来摆脱它,但我不想这样做。我主要关心的是使Caller更健壮。您可能想要引入重试逻辑、超时等。

慕桂英3389331

为了使您的设计工作,您可以尝试下一种方法:static void Main(string[] args){&nbsp; &nbsp; var builder = new ContainerBuilder();&nbsp; &nbsp; builder.RegisterType<BackUpMechanismA>().Keyed<IFileBackup>("A");&nbsp; &nbsp; builder.RegisterType<BackUpMechanismB>().Keyed<IFileBackup>("B");&nbsp; &nbsp; builder.RegisterType<Caller>()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .WithParameter((p, ctx) => p.Position == 0, (p, ctx) => ctx.ResolveKeyed<IFileBackup>("A"))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .WithParameter((p, ctx) => p.Position == 1, (p, ctx) => ctx.ResolveKeyed<IFileBackup>("B"));&nbsp; &nbsp; IContainer container = builder.Build();&nbsp; &nbsp; var caller = container.Resolve<Caller>();&nbsp; &nbsp; Console.ReadKey();}但是在我看来,您可能在这里不需要这样的多态性。实现这样的东西会更加明显和描述性:public async Task BackupFile(byte[] file){&nbsp; &nbsp; try&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; await BackUpToAmazonS3(file);&nbsp; &nbsp; }&nbsp; &nbsp; catch (AmazonS3LoadingException)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; await BackUpToLocalDisk(file);&nbsp; &nbsp; }}在这个例子中,很明显发生了什么。在那里BackUpToAmazonS3你可以使用一些注入AmazonS3FileBackUp和BackUpToLocalDisk使用中的LocalDiskFileBackUp东西。关键是当您不打算更改实现时,您不需要多态性。在您的上下文中应该清楚吗?您尝试将备份放入某个远程存储,然后,如果失败,则放入本地磁盘。您无需在此处隐藏含义。这是你的逻辑,应该很清楚,当你阅读代码时,我想。希望能帮助到你。

红糖糍粑

尝试使用名称注册,然后使用名称解析:builder.RegisterType<BackupMechanismA>().Named<IFileBackup>("BackUpMechanismA");builder.RegisterType<BackupMechanismB>().Named<IFileBackUp>("BackUpMechanismB");_backupA = container.ResolveNamed<IFileBackUp>&nbsp;("BackUpMechanismA");&nbsp;_backupB = container.ResolveNamed<IFileBackUp>&nbsp;("BackUpMechanismB");在运行时解析实例,而不是通过构造函数注入。这将让您根据需要解析为相应的类型。让我知道这个是否奏效。
随时随地看视频慕课网APP
我要回答