类类型的字典
本文关键字:字典 类型 | 更新日期: 2023-09-27 18:21:55
我有一组类,每个类都可以使用外部应用程序打开不同类型的文件,并告诉该应用程序将文件打印到特定的打印机。这些类都继承了一个公共抽象类和一个接口。
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<string, Type> FileConverters;
static Printer()
{
FileConverters = new Dictionary<string, Type>();
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<string, Type> FileConverters
限制为实现IApplicationPrinter的值来提高类型安全性?换句话说,像这样的事情可能吗:
private static Dictionary<string, T> FileConverters where T: IApplicationPrinter;
更新:由于以下两个原因,我不想存储实例:
- 每个类可以处理几种不同的文件类型(请参见
string[] PrintableExtensions
)。字典将扩展名存储为关键字。创建和存储同一类的多个独立实例没有任何实用性 - 每个打印机类都使用COM API和Office Interop来创建第三方应用程序的实例。最好在需要时为打印作业创建每个类的新实例,然后垃圾收集器可以进行清理
我的做法略有不同:
private Dictionary<String, Func<IApplicationPrinter>> _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
。
不直接-请记住,您放在字典中的是Type
对象,而不是实现IApplicationPrinter
的对象。
这里最好的选择可能是通过检查type.GetInterface("IApplicationPrinter")
是否返回null来检查添加到字典中的每个类型是否实现IApplicationPrinter
。
如果使用Dictionary<string, IApplicationPrinter>
,这并不意味着每个string
必须有不同的实例,其中几个实例可以共享同一个实例。
如果你不想那样做,你可以把工厂存储在字典里。工厂可以是一个实现接口的对象(类似于IApplicationPrinterFactory
),也可以只是一个可以创建对象的委托。在您的情况下,这将是Dictionary<string, Func<IApplicationPrinter>>
。这样做是完全类型安全的。要添加到字典中,您可以执行以下操作:
FileConverters = new Dictionary<string, Func<IApplicationPrinter>>();
Func<IApplicationPrinter> printerFactory = () => new WordPrinter();
foreach (string ext in WordPrinter.PrintableExtensions)
FileConverters.Add(ext, printerFactory);
如果你确定你想要Dictionary<string, Type>
,那么没有办法限制它,所以那里的所有类型都实现IApplicationPrinter
。您可以创建自己的字典,在添加时检查类型。这并不能保证编译时的安全,但它使运行时更加安全。
class TypeDictionary : IDictionary<string, Type>
{
private readonly Type m_typeToLimit;
readonly IDictionary<string, Type> m_dictionary =
new Dictionary<string, Type>();
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
}
使用属性可以限制类型。有两种方法可以对此进行存档。
- 在类型的构造函数中引发异常(请参阅此链接)
- 使用抽象类而不是接口,这允许您通过使用受保护的属性来限制您的类(此时,您需要为该属性创建一个基类,以便从类型字典中访问它),该属性只能应用于指定的类
现在,您只需检查特定类是否具有PrinterExtensions
属性,并访问其检索所有扩展的属性,或者调用直接注册所有扩展的方法。