英文:
Dependency Injector and choosing the a class at runtime
问题
以下是您要翻译的内容:
我的简单应用程序可以使用依赖注入以一种或另一种方式记录某些内容。每个记录器都是一个类,派生自接口 ILogger
。类 LoggerFactory
决定返回哪个记录器给幻想应用程序 TheAmazingApp
,然后应用程序使用该具体记录器。
问题:如何实现用户选择,即在运行时使用 Autofac 加载适当的类?
下面的控制台应用程序可以编译。如果您愿意,请观察“LoggerFactory”中的注释(最后一个类/文件) - 重申我的挑战。
Main.cs
using Autofac;
namespace DepInjection
{
internal class Program
{
static void Main(string[] args)
{
var Builder = new ContainerBuilder();
//开始配置
Builder.RegisterType<TheAmazingApp>();
Builder.RegisterType<LoggerFactory>();
Builder.RegisterType<NormalLogger>().As<ILogger>();
Builder.RegisterType<VerboseLogger>().As<ILogger>();
//结束配置
var Container = Builder.Build();
using (var scope = Container.BeginLifetimeScope())
{
scope.Resolve<TheAmazingApp>().AmazeUs();
}
}
}
}
TheAmazingApp.cs
namespace DepInjection
{
public class TheAmazingApp
{
private ILogger _logger;
public TheAmazingApp()
{
_logger = LoggerFactory.GetLogger();
}
public void AmazeUs() => _logger.Logger();
}
}
ILogger.cs
namespace DepInjection
{
public interface ILogger
{
public void Logger();
}
}
VerboseLogger.cs
namespace DepInjection
{
public class VerboseLogger : ILogger
{
public void Logger() =>
Console.WriteLine("Verbose Log");
}
}
NormalLogger.cs
namespace DepInjection
{
public class NormalLogger : ILogger
{
public void Logger() =>
Console.WriteLine("Normal Log");
}
}
LoggerFactory.cs
namespace DepInjection
{
public class LoggerFactory
{
private static string? _choice;
public static ILogger GetLogger()
{
//模拟运行时用户选择的结果
_choice = "other";
//... 但我如何使用依赖注入器来实现这一点呢?
return _choice == "1" ? new NormalLogger() :
new VerboseLogger();
}
}
}
英文:
My simple application could, using dependency injection, log something one way or the other way. Each logger is a class, derived from an interface ILogger
. The class LoggerFactory
decides which logger to return to the fantasy application TheAmazingApp
and then the application uses that concrete logger.
Question: how to implement this user choice, i.e. loading the proper class at run time using Autofac?
The console app below can be compiled. Observe the comment in "LoggerFactory" if you will (last class/file) - reiterating my challenge.
Main.cs
using Autofac;
namespace DepInjection
{
internal class Program
{
static void Main(string[] args)
{
var Builder = new ContainerBuilder();
//Start configure
Builder.RegisterType<TheAmazingApp>();
Builder.RegisterType<LoggerFactory>();
Builder.RegisterType<NormalLogger>().As<ILogger>();
Builder.RegisterType<VerboseLogger>().As<ILogger>();
//End configure
var Container = Builder.Build();
using (var scope = Container.BeginLifetimeScope())
{
scope.Resolve<TheAmazingApp>().AmazeUs();
}
}
}
}
TheAmazingApp.cs
namespace DepInjection
{
public class TheAmazingApp
{
private ILogger _logger;
public TheAmazingApp()
{
_logger = LoggerFactory.GetLogger();
}
public void AmazeUs() => _logger.Logger();
}
}
ILogger.cs
namespace DepInjection
{
public interface ILogger
{
public void Logger();
}
}
VerboseLogger.cs
namespace DepInjection
{
public class VerboseLogger : ILogger
{
public void Logger() =>
Console.WriteLine("Verbose Log");
}
}
NormalLogger.cs
namespace DepInjection
{
public class NormalLogger : ILogger
{
public void Logger() =>
Console.WriteLine("Normal Log");
}
}
LoggerFactory.cs
namespace DepInjection
{
public class LoggerFactory
{
private static string? _choice;
public static ILogger GetLogger()
{
//emulates the result of a user choice at run time
_choice = "other";
//...but how do I implement this using a dependency injector?
return _choice == "1" ? new NormalLogger() :
new VerboseLogger();
}
}
}
答案1
得分: 1
以下是您要翻译的内容:
有多种解决方法,但其中一种最方便的方法是创建一个代理记录器实现,以进行选择,例如:
public class UserChoiceLogger : ILogger // 这是您的代理
{
private readonly NormalLogger normal;
private readonly VerboseLogger verbose;
public UserChoiceLogger(NormalLogger normal, VerboseLogger verbose)
{
this.normal = normal;
this.verbose = verbose;
}
public void Logger()
{
// 模拟运行时用户选择的结果
_choice = "other";
if (_choice == "1")
normal.Logger();
else
verbose.Logger();
}
}
在其中将 TheAmazingApp
更改为仅使用构造函数注入:
public class TheAmazingApp
{
private ILogger logger;
public TheAmazingApp(ILogger logger)
{
this.logger = logger;
}
public void AmazeUs() => logger.Logger();
}
可以如下注册:
var Builder = new ContainerBuilder();
//开始配置
Builder.RegisterType<TheAmazingApp>();
Builder.RegisterType<NormalLogger>();
Builder.RegisterType<VerboseLogger>();
Builder.RegisterType<UserChoiceLogger>().As<ILogger>();
//结束配置
var Container = Builder.Build();
using (var scope = Container.BeginLifetimeScope())
{
scope.Resolve<TheAmazingApp>().AmazeUs();
}
请注意,减少了需要工厂的需求。工厂通常是多余的。
英文:
There are multiple ways to solve this, but one of the most convenient methods is to create a proxy logger implementation that makes the choice, e.g.:
public class UserChoiceLogger : ILogger // This is your proxy
{
private readonly NormalLogger normal;
private readonly VerboseLogger verbose;
public UserChoiceLogger(NormalLogger normal, VerboseLogger verbose)
{
this.normal = normal;
this.verbose = verbose;
}
public void Logger()
{
//emulates the result of a user choice at run time
_choice = "other";
if (_choice == "1")
normal.Logger();
else
verbose.Logger();
}
}
Where you change TheAmazingApp
to simply use constructor injection:
public class TheAmazingApp
{
private ILogger logger;
public TheAmazingApp(ILogger logger)
{
this.logger = logger;
}
public void AmazeUs() => logger.Logger();
}
This can be registered as follows:
var Builder = new ContainerBuilder();
//Start configure
Builder.RegisterType<TheAmazingApp>();
Builder.RegisterType<NormalLogger>();
Builder.RegisterType<VerboseLogger>();
Builder.RegisterType<UserChoiceLogger>().As<ILogger>();
//End configure
var Container = Builder.Build();
using (var scope = Container.BeginLifetimeScope())
{
scope.Resolve<TheAmazingApp>().AmazeUs();
}
Notice how the need to have a factory deminished. Factories are often redundant.
答案2
得分: 0
"After study it appears that the named and keyed services could provide an answer. Something like...
container.ResolveNamed
...is possible and this gives options. Using an explicit keyed method would resemble this:
container.ResolveKeyed
Haven't figured out a pretty way for the totality of the code, so I can't use this. But it's a step in the right direction. And like user @eocron mentioned, a Dep. Injector isn't really cut out for this task."
英文:
After study it appears that the named and keyed services could provide an answer.Something like...
container.ResolveNamed<NormalLogger>("Normal") //"Normal" is returned via user input
...is possible and this gives options. Using an explicit keyed method would resemble this:
container.ResolveKeyed<NormalLogger>(Logger.Normal) //Logger.Normal is returned via user input
Haven't figured out a pretty way for the totality of the code, so I can't use this. But it's a step in the right direction. And like user @eocron mentioned, a Dep. Injector isn't really cut out for this task.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论