Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dictionary of class types

Tags:

c#

generics

I have a set of classes, each of which can open different types of files using an external application and tell that application to print the file to a particular printer. The classes all inherit a common abstract class and an interface.

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" }; } }
    ...
}

I am trying to create a Dictionary of printable file extensions and corresponding Types of classes that can print such files. I do not want to instantiate the classes in the dictionary.

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();
    }
}

Is there any way to make Dictionary<string, Type> FileConverters more type-safe, by restricting it to values that implement IApplicationPrinter? In other words, is something like this possible:

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

Update: I do not want to store instances for the following two reasons:

  1. Each class can handle several different file types (see string[] PrintableExtensions). The dictionary stores extensions as keys. There is no utility in creating and storing multiple separates instance of the same class.
  2. Each printer class uses COM API and Office Interop to create instances of third-party applications. It's better that a new instance of each class is created for a print job when so required, and that the garbage collector can clean up afterwards.
like image 410
Serge Avatar asked Jan 28 '12 10:01

Serge


2 Answers

I would do it slightly differently:

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);
}

This method will not store instances in the dictionary as you require, and will create you a new instance each time you call GetPrinterFor. It also is more strongly typed as the return type of the Func<> has to be an IApplicationPrinter.

like image 182
Pondidum Avatar answered Sep 20 '22 07:09

Pondidum


Not directly - remember that the things you're putting in your dictionary are Type objects, not objects that implement IApplicationPrinter.

Probably the best option here is to check that each type you add to your dictionary implements IApplicationPrinter, by checking whether type.GetInterface("IApplicationPrinter") returns null or not.

like image 22
thomson_matt Avatar answered Sep 21 '22 07:09

thomson_matt