类类型字典

时间:2012-01-28 10:58:59

标签: c# generics

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

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;

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

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

4 个答案:

答案 0 :(得分:4)

我的做法略有不同:

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

答案 1 :(得分:2)

不直接 - 请记住,您在字典中添加的内容是Type个对象,而不是实现IApplicationPrinter的对象。

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

答案 2 :(得分:1)

如果您使用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
}

答案 3 :(得分:0)

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

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

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

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

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