0

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

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

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

4

3 回答 3

0

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

简单的插件机制示例

所以讨论

于 2013-10-10T13:49:44.503 回答
0

取决于读者的同谋,您可能决定使用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[] {});
            }

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

于 2013-10-10T14:28:33.370 回答
0

你可以使用反射。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;

}
于 2013-10-10T14:07:34.713 回答