根据类的名称获取类的实例



我在appsettings中设置了StrategyName。Json,表示策略类的名称。我需要得到它的一个实例。

ITradingStrategy _tradingStrategy = StrategyUtils.GetStrategyInstance(logger, _tradeOptions.StrategyName)

等于

ITradingStrategy _tradingStrategy = new RsiStrategy(logger);

有可能以更好的方式制作吗?它可以工作,但看起来很丑。既然我们一开始就知道策略名称(从appsettings.json),那么应该有一种方法可以在更好的ASP中获得它。. NET Core方式。也许是一些很酷的扩展方法,我不知道。

appsettings.json

{
"TradeConfiguration": {
"StrategyName": "RsiStrategy",
...
}
}

代码
public class LiveTradeManager : ITradeManager
{
private readonly ILogger _logger;
private readonly IExchangeClient _exchangeClient;
private readonly ITradingStrategy _tradingStrategy;
private readonly ExchangeOptions _exchangeOptions;
private readonly TradeOptions _tradeOptions;
public LiveTradeManager(ILogger logger, IConfiguration configuration, IExchangeClient exchangeClient)
{
_logger = logger;
_exchangeClient = exchangeClient;
_exchangeOptions = configuration.GetSection("ExchangeConfiguration").Get<ExchangeOptions>();
_tradeOptions = configuration.GetSection("TradeConfiguration").Get<TradeOptions>();
_tradingStrategy = StrategyUtils.GetStrategyInstance(logger, _tradeOptions.StrategyName); // This is the questioned line
}
}
public static ITradingStrategy GetStrategyInstance(ILogger logger, string strategyName)
{
var strategyType = Assembly.GetAssembly(typeof(StrategyBase))
.GetTypes().FirstOrDefault(type => type.IsSubclassOf(typeof(StrategyBase)) && type.Name.Equals(strategyName));
if (strategyType == null)
{
throw new ArgumentException($"The strategy "{strategyName}" could not be found.", nameof(strategyName));
}
var strategy = Activator.CreateInstance(strategyType, logger) as ITradingStrategy;
return strategy;
}
// Strategies
public interface ITradingStrategy
{
IReadOnlyList<TradeAdvice> Prepare(IReadOnlyList<OHLCV> candles);
}
public abstract class StrategyBase : ITradingStrategy
{
private readonly ILogger _logger;
protected StrategyBase(ILogger logger)
{
_logger = logger;
}

public abstract IReadOnlyList<TradeAdvice> Prepare(IReadOnlyList<OHLCV> candles);
}
public class RsiStrategy : StrategyBase
{
private readonly ILogger _logger;
public RsiStrategy(ILogger logger) : base(logger)
{
_logger = logger;
}

public override IReadOnlyList<TradeAdvice> Prepare(IReadOnlyList<OHLCV> candles)
{
... _logger.Information("Test");
}
}
// Main
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
})
.ConfigureServices((hostingContext, services) =>
{
services.AddSingleton(
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(hostingContext.Configuration)
.CreateLogger());
services.AddSingleton<ITradeManager, LiveTradeManager>();
services.AddSingleton<IExchangeClient, BinanceSpotClient>();

services.AddHostedService<LifetimeEventsHostedService>();
})
.UseSerilog();
}

你的问题可以通过多种方式解决,使用反射将是最后一种。

从你的问题陈述中,我认为你有多个策略分类实现ITradingStrategy接口,你的配置值从appsettings.json文件决定使用哪种策略。

您可以在这里使用的一种方法是使用factory来初始化基于配置值的适当策略类。

下面是工厂类和接口,它将根据传递给它的策略名称创建策略类对象。

public interface IStrategyFactory
{
ITradingStrategy GetStrategy(string strategyName);
}
public class StrategyFactory : IStrategyFactory
{
private IServiceProvider _serviceProvider;
public StrategyFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public ITradingStrategy GetStrategy(string strategyName)
{
switch (strategyName)
{
case "Rsi":
// Resolve RsiStrategy object from the serviceProvider.
return _serviceProvider.GetService<RsiStrategy>();
case "Dmi":
// Resolve DmiStrategy object from the serviceProvider.
return _serviceProvider.GetService<DmiStrategy>();
default:
return null;
}
}
}

该策略现在可以在控制器中使用,并通过传递从配置中检索的策略名称来调用其GetStrategy方法。

public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
// Strategy factory.
private IStrategyFactory _strategyFactory;
// Configuration
private IConfiguration _configuration;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration, IStrategyFactory strategyFactory)
{
_logger = logger;
_strategyFactory = strategyFactory;
_configuration = configuration;
}
public IActionResult Index()
{
// Get Configuration value "StrategyName" from configuration.
// In your case this will be your own custom configuration.
var strategyName = _configuration.GetValue<string>("StrategyName");
// Pass strategyName to GetStrategy Method.
var strategy = _strategyFactory.GetStrategy(strategyName);
// Call Prepare method on the retrieved strategy object.
ViewBag.PreparedList = strategy.Prepare(new List<OHLCV>());
return View();
}
}

要让上面的代码工作,你需要注册一个类为serviceCollection的策略。

services.AddSingleton<RsiStrategy>();
services.AddSingleton<DmiStrategy>();

还有StrategyFactory

services.AddSingleton<IStrategyFactory, StrategyFactory>();

编辑

根据您下面的评论,您需要能够解决策略类型,而不需要在创建新类型时在DI中注册它们的额外开销,也不需要在工厂中进行更改。

您需要为此使用反射。使用反射,您可以确定要在DI中注册的类型。如下。

//Get all the types which are inheriting from StrategyBase class from the assembly.
var strategyTypes = Assembly.GetAssembly(typeof(StrategyBase))
?.GetTypes()
.Where(type => type.IsSubclassOf(typeof(StrategyBase)));
if (strategyTypes != null)
{
//Loop thru the types collection and register them in serviceCollection.
foreach (var type in strategyTypes)
{
services.Add(new ServiceDescriptor(typeof(StrategyBase), type, ServiceLifetime.Singleton));
}
}
使用上面的代码,从StrategyBase继承的所有类型都在serviceCollection中注册了。现在,使用serviceprovider,我们可以获得所有已注册的实例,并查找具有正确的strategyName的实例。

所以工厂的GetStrategy方法如下所示:

public ITradingStrategy GetStrategy(string strategyName)
{
var strategies = _serviceProvider.GetServices<StrategyBase>();
var strategy = strategies.FirstOrDefault(s => s.GetType().Name == strategyName);
if (strategy == null)
{
throw new ArgumentException($"The strategy "{strategyName}" could not be found.", nameof(strategyName));
}
return strategy;
}

我希望这将帮助你解决你的问题。

最新更新