类类型字典

我有一组类,每个类都可以使用外部应用程序打开不同类型的文件,并告诉该应用程序将文件打印到特定的打印机。 这些类都inheritance了一个公共抽象类和一个接口。

internal interface IApplicationPrinter : IDisposable { string ApplicationExe { get; } string ApplicationName { get; } string[] PrintableExtensions { get; } IApplicationPrinter CreateInstance(string Filename, string Printer); void Print(); bool ExitApplicationAfterPrint { get; set; } bool WaitApplicationExitOnPrint { get; set; } System.IO.FileInfo PdfFile { get; protected set; } } internal abstract class ApplicationPrinter : IApplicationPrinter { ... } internal class WordPrinter : ApplicationPrinter { internal static string[] PrintableExtensions { get { return new string[]{".doc", ".docx" }; } } ... } internal class ExcelPrinter : ApplicationPrinter { internal static string[] PrintableExtensions { get { return new string[]{".xls", ".xlsx" }; } } ... } 

我正在尝试创建一个可打印文件扩展名的Dictionary和相应的可以打印此类文件的类的Type 。 我不想在字典中实例化类。

 private static Dictionary FileConverters; static Printer() { FileConverters = new Dictionary(); foreach (string ext in WordPrinter.PrintableExtensions) { FileConverters.Add(ext, typeof(WordPrinter)); } string filename = "textfile.txt"; string extension = filename.Substring(filename.LastIndexOf(".")); if (FileConverters.ContainsKey(extension)) { IApplicationPrinter printer = ((IApplicationPrinter)FileConverters[extension]).CreateInstance(filename, "Printer"); printer.Print(); } } 

有没有办法让Dictionary FileConverters更加类型安全,通过将其限制为实现IApplicationPrinter的值? 换句话说,这样的事情是可能的:

 private static Dictionary FileConverters where T: IApplicationPrinter; 

更新:由于以下两个原因,我不想存储实例:

  1. 每个类都可以处理几种不同的文件类型(请参阅string[] PrintableExtensions )。 字典将扩展名存储为键。 在创建和存储同一类的多个分隔实例时没有任何实用工具。
  2. 每个打印机类都使用COM API和Office Interop来创建第三方应用程序的实例。 最好在需要时为打印作业创建每个类的新实例,然后垃圾收集器可以清理。

不直接 – 请记住,您在字典中放置的内容是Type对象,而不是实现IApplicationPrinter对象。

这里最好的选择可能是通过检查type.GetInterface("IApplicationPrinter")是否返回null来检查添加到字典中的每个类型是否实现了IApplicationPrinter

我会做的略有不同:

 private Dictionary> _converters; public void Initialise() { foreach (string ext in WordPrinter.PrintableExtensions) { _converters.Add(ext, () => new WordPrinter()); } } public IApplicationPrinter GetPrinterFor(String extension) { if (_converters.ContainsKey(extension)) //case sensitive! { return _converters[extension].Invoke(); } throw new PrinterNotFoundException(extension); } 

此方法不会根据需要在字典中存储实例,并且每次调用GetPrinterFor时都会为您创建一个新实例。 它的类型也更强,因为Func<>的返回类型必须是IApplicationPrinter

如果您使用了Dictionary ,那么这并不意味着您必须为每个string设置不同的实例,其中几个可以共享同一个实例。

如果您不想这样做,可以将工厂存储在字典中。 工厂可以是实现接口的对象(类似于IApplicationPrinterFactory ),或者只是可以创建对象的委托。 在您的情况下,这将是Dictionary> 。 这样做是完全类型安全的。 要添加到字典中,您可以执行以下操作:

 FileConverters = new Dictionary>(); Func printerFactory = () => new WordPrinter(); foreach (string ext in WordPrinter.PrintableExtensions) FileConverters.Add(ext, printerFactory); 

如果您确定需要Dictionary ,则无法限制它,因此其中的所有类型都实现了IApplicationPrinter 。 你可以做的是创建自己的字典,在添加时检查类型。 这不会使编译时安全,但它使运行时更安全。

 class TypeDictionary : IDictionary { private readonly Type m_typeToLimit; readonly IDictionary m_dictionary = new Dictionary(); public TypeDictionary(Type typeToLimit) { m_typeToLimit = typeToLimit; } public void Add(string key, Type value) { if (!m_typeToLimit.IsAssignableFrom(value)) throw new InvalidOperationException(); m_dictionary.Add(key, value); } public int Count { get { return m_dictionary.Count; } } public void Clear() { m_dictionary.Clear(); } // the rest of members of IDictionary } 

首先,您需要更改访问扩展数组的方法,因为如果不创建实例,则无法访问您的属性。 我会使用自定义属性来告诉您的类型字典每个类支持哪个扩展名。 这将是这样的:

 [PrinterExtensions("txt", "doc")] public class WordPrinter { .... // Code goes here } 

使用该属性可以限制类型。 存档有两种方法。

  • 在类型的构造函数中抛出exception(请参阅此链接 )
  • 使用抽象类而不是接口,这允许您通过使用受保护的属性来限制您的类(此时您需要为属性创建一个基类来从类型字典中访问它),这只能应用于devired类。

现在您可以只检查特定类是否具有PrinterExtensions属性并访问它的属性,该属性检索所有扩展或调用直接注册所有扩展的方法。