在C#中,可以无须实例化地实例化在命名空间中找到的所有类吗?
我有一个脚本类列表。全部在同一文件夹中。所有实现我的基类BaseScript
。我需要每个小时都运行一次。我已经有时间和活动了。我也已经有一个List<BaseScript> ListOfScripts
。但是现在我正在这样做:
public class Program
{
private static List<BaseScript> ListOfScrpits { get; set; }
static void Main(string[] args)
{
ListOfScrpits = new List<BaseScript>
{
new DoThis(),
new DoThat(),
new DoSomethingElse()
};
Timer timer = new Timer();
timer.Interval = 600000; // 10 * 60 seconds
timer.Elapsed += new ElapsedEventHandler(OnTimer);
timer.Start();
}
private static void OnTimer(object sender, ElapsedEventArgs e)
{
// TODO
}
}
我可以动态创建脚本列表吗?
根据您的要求,以及您的最新评论建议您接受反思,我建议以下内容:
// add any assemblies you want to discover scripts in here
var assemblies = new[] { System.Reflection.Assembly.GetExecutingAssembly() };
var scriptTypes = assemblies
// get the declared types for each and every assembly
.SelectMany(a => a.GetTypes())
// filter so that we ignore BaseClass, only accept types assignable to base class,
// don't accept abstract classes and only accept types with a parameterless constructor
.Where(t => t != typeof(BaseClass) && typeof(BaseClass).IsAssignableFrom(t) && !t.IsAbstract && t.GetConstructors().Any(c => c.GetParameters().Length == 0));
// create an instance of each type and construct a list from it
var scripts = scriptTypes
.Select(t => (BaseClass)Activator.CreateInstance(t))
.ToList();
我建议在这里使用依赖注入容器为您处理类的实例化。在这里使用DI的好处在于,它可以使您的类具有更大的可扩展性。例如,如果您最终在实现BaseScript
的任何类中都需要一些其他依赖项,则只要注册它们,DI容器就会为您解决这些依赖项。
您可以通过.NET Core中提供的ServiceCollection轻松完成此操作:
class Program
{
private static List<BaseScript> ListOfScrpits { get; set; }
static void Main(string[] args)
{
ServiceProvider serviceProvider = new ServiceCollection()
.AddTransient<BaseScript, DoThis>()
.AddTransient<BaseScript, DoThis>()
.AddTransient<BaseScript, DoSomethingElse>()
.BuildServiceProvider();
ListOfScrpits = serviceProvider.GetServices<BaseScript>().ToList();
foreach (BaseScript script in ListOfScrpits)
Console.WriteLine(script.GetType().Name);
Console.ReadLine();
}
}
您甚至可以注册BaseScript
的所有实例(使用反射,如其他答案所示),以便在添加新实现时将自动解析并使用新的实现。只需将serviceProvider
位更改为:
System.Reflection.Assembly[] assemblies = new[] { System.Reflection.Assembly.GetExecutingAssembly() };
IEnumerable<Type> scriptTypes = assemblies
.SelectMany(a => a.GetTypes())
.Where(t => t != typeof(BaseScript) && typeof(BaseScript).IsAssignableFrom(t) && !t.IsAbstract);
ServiceCollection serviceCollection = new ServiceCollection();
foreach (Type scriptType in scriptTypes)
serviceCollection.AddTransient(typeof(BaseScript), scriptType);
ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
所以这些类将所有这些附加逻辑内置到其构造函数中?也许您可以做一些类似静态方法的事情?您甚至可以列出代表。脚本列表是否经常更改?也许保留一个保存列表的配置文件可以解决问题?
public class ScriptRunner
{
public static List<Delegate> Scripts { get; set; }
public static void RunScripts()
{
foreach (var script in Scripts)
{
script.DynamicInvoke();
}
}
}
如果您关心执行动作/功能/委托的严格顺序,那么您当然可以使用不同的分组,字典或数组列表。
或者,您可以为每个类使用静态构造函数。包含逻辑的构造函数(此处不是面向对象的设计,但您正在执行功能脚本),将在EXE加载时调用该逻辑。
使用此方法,您可以每隔几个小时使用Windows Task Scheduler以设置的间隔调用EXE。这意味着您可以每隔几个小时重新调用一次静态构造函数。这也将避免需要使用Thread.Sleep或其他C#替代方法暂停应用程序。
更多关于静态构造函数的信息: