在C#中可以实例化在命名空间中找到的所有类吗?

问题描述 投票:0回答:3

在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
        }
    }

我可以动态创建脚本列表吗?

c#
3个回答
1
投票

根据您的要求,以及您的最新评论建议您接受反思,我建议以下内容:

// 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();

1
投票

我建议在这里使用依赖注入容器为您处理类的实例化。在这里使用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();

-1
投票

所以这些类将所有这些附加逻辑内置到其构造函数中?也许您可以做一些类似静态方法的事情?您甚至可以列出代表。脚本列表是否经常更改?也许保留一个保存列表的配置文件可以解决问题?


    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#替代方法暂停应用程序。

更多关于静态构造函数的信息:

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors

© www.soinside.com 2019 - 2024. All rights reserved.