在 C# 中添加对动态文件加载程序的支持

本文关键字:加载 程序 支持 文件 动态 添加 | 更新日期: 2023-09-27 18:34:49

如果我有一个 C# 控制台应用程序,它可以读取某种格式的文件并将其转换为业务对象,我通过具有 IReader 接口来设计它,以便我可以支持不同的格式,例如 XML、CSV、管道分隔等,并且每种文件格式都有不同的具体类。

如果要求能够动态加载新的文件读取器(新格式(,而无需重新编译,有没有办法做到这一点?

我能想到的唯一方法是以某种方式使用 XSD 或 reg 表达式,但在我看来应该有更好的解决方案

在 C# 中添加对动态文件加载程序的支持

这听起来像您想要一个插件机制来动态加载您的 IReaders。有很多例子。

简单插件机制示例

所以讨论

您可以使用反射。 IReader 的每个实现都可以放在不同的 DLL 中。 您还将创建一个属性来标记 IReader 的每个实现,以声明它处理的文件格式。

public sealed class InputFormatAttribute : Attribute
{
    private string _format;
    public string Format
    {
        get { return format; }
    }
    public InputFormatAttribute(string format)
    {
        _format = format;
    }
}
[InputFormat("CSV")]
public class CSVReader : IReader
{
    // your CSV parsing code here
    public BusinessObject Parse(string file)
    {}
}

BusinessObject LoadFile(string fileName)
{
    BusinessObject result = null;
    DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
    FileInfo[] pluginList = dirInfo.GetFiles("*.DLL");
    foreach (FileInfo plugin in pluginList)
    {
        System.Reflection.Assembly assem = System.Reflection.Assembly.LoadFile(fileInfo.FullName);
        Type[] types = assem.GetTypes();
        Type type = types.First(t => t.BaseType == "IReader");
        object[] custAttrib = type.GetCustomAttributes(typeof(InputFormatAttribute), false);
        InputFormatAttribute at = (InputFormatAttribute)custAttrib[0];
        if (at.Format.Equals(Path.GetExtension(fileName).Substring(1), StringComparison.CurrentCultureIgnoreCase))
        {
            IReader reader = (IReader)assem.CreateInstance(type.FullName);
            return reader.Parse(fileName);
        }
    }
    // got here because not matching plugin found
    return null;
}

根据读者的同谋,你可以决定使用CodeDom让某人直接编写代码。简短示例:

            // create compiler
            CodeDomProvider provider = CSharpCodeProvider.CreateProvider("C#");
            CompilerParameters options = new CompilerParameters();
            // add more references if needed
            options.ReferencedAssemblies.Add("system.dll");
            options.GenerateExecutable = false;
            options.GenerateInMemory = true;
            // compile the code
            string source = "using System;namespace Bla {public class Blabla { public static bool Test { return false; }}}";
            CompilerResults result = provider.CompileAssemblyFromSource(options, source);
            if (!result.Errors.HasErrors)
            {
                Assembly assembly = result.CompiledAssembly;
                // instance can be saved and then reused whenever you need to run the code
                var instance = assembly.CreateInstance("Bla.Blabla");
                // running some method
                MethodInfo method = instance.GetType().GetMethod("Test"));
                var result = (bool)method.Invoke(_formulas, new object[] {});
            }

但是您可能必须提供某种编辑器或部分完成任务(因此只需要编写必要的代码(。