【问题标题】:Dictionary of class types类类型字典
【发布时间】:2023-04-06 00:03:01
【问题描述】:

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

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 和可以打印此类文件的相应类Types。我确实想实例化字典中的类。

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&lt;string, Type&gt; FileConverters 限制为实现IApplicationPrinter 的值来使Dictionary&lt;string, Type&gt; FileConverters 更加类型安全?换句话说,这样的事情是否可能:

private static Dictionary<string, T> FileConverters where T: IApplicationPrinter;

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

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

【问题讨论】:

  • 为什么不想在字典中存储实例?

标签: c# generics


【解决方案1】:

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

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

【讨论】:

  • 我认为最好尽可能避免使用字符串而不是类型。在这里是可能的。
  • 你是对的 - typeof(IApplicationPrinter).Name 会比“IApplicationPrinter”更好。
  • 我的意思更像是typeof(IApplicationPrinter).IsAssignableFrom(type)。完全安全,不使用任何字符串。
【解决方案2】:

我会稍微不同:

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&lt;&gt; 的返回类型必须是IApplicationPrinter

【讨论】:

  • 我一直在寻找这个解决方案!感谢 Pondidum,我的代码中不再需要巨大的 if () new A(); else if () new B(); else if () new C(); else if () new...
【解决方案3】:

如果您使用Dictionary&lt;string, IApplicationPrinter&gt;,这并不意味着您必须为每个string 拥有不同的实例,它们中的几个可以共享同一个实例。

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

FileConverters = new Dictionary<string, Func<IApplicationPrinter>>();

Func<IApplicationPrinter> printerFactory = () => new WordPrinter();

foreach (string ext in WordPrinter.PrintableExtensions)
    FileConverters.Add(ext, printerFactory);

如果你确定你想要Dictionary&lt;string, Type&gt;,没有办法限制它,所以那里的所有类型都实现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
}

【讨论】:

    【解决方案4】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-18
      • 1970-01-01
      • 2022-06-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多