如果我有一个 C# 中的控制台应用程序,它可以读取某种格式的文件并将它们转换为业务对象,我通过一个 IReader 接口来设计它,以便我可以支持不同的格式,例如 XML、CSV、管道分隔等,并且有每种文件格式都有不同的具体类。
如果要求能够动态加载新的文件阅读器(新格式)而无需重新编译,有没有办法可以做到这一点?
我能想到的唯一方法是以某种方式使用 XSD 或 reg 表达式,但在我看来应该有更好的解决方案
如果我有一个 C# 中的控制台应用程序,它可以读取某种格式的文件并将它们转换为业务对象,我通过一个 IReader 接口来设计它,以便我可以支持不同的格式,例如 XML、CSV、管道分隔等,并且有每种文件格式都有不同的具体类。
如果要求能够动态加载新的文件阅读器(新格式)而无需重新编译,有没有办法可以做到这一点?
我能想到的唯一方法是以某种方式使用 XSD 或 reg 表达式,但在我看来应该有更好的解决方案
取决于读者的同谋,您可能决定使用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[] {});
}
但可能您必须提供某种编辑器或部分完成任务(因此只需要编写必要的代码)。
你可以使用反射。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;
}