2012-01-28 62 views
6

我有一組類,每個類都可以使用外部應用程序打開不同類型的文件,並告訴應用程序將文件打印到特定的打印機。這些類都繼承了一個通用的抽象類和一個接口。類的字典

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來創建第三方應用程序的實例。如果需要,最好爲打印作業創建每個類的新實例,並且垃圾收集器可以在之後進行清理。
+0

爲什麼你不想在字典中存儲實例? – svick 2012-01-28 11:13:27

回答

2

不是直接記住您要放入字典的東西是Type對象而不是實現IApplicationPrinter的對象。

這裏最好的選擇可能是通過檢查type.GetInterface("IApplicationPrinter")是否返回null來檢查您添加到字典中的每種類型是否實現IApplicationPrinter

+0

我認爲最好避免使用字符串,而不要使用可能的類型。這是可能的。 – svick 2012-01-28 11:58:28

+0

你是對的 - typeof(IApplicationPrinter).Name會比「IApplicationPrinter」更好。 – 2012-01-29 14:11:11

+0

我的意思是更多沿'typeof(IApplicationPrinter).IsAssignableFrom(type)'的行。完全安全,沒有使用任何字符串。 – svick 2012-01-29 14:27:17

1

如果您使用Dictionary<string, IApplicationPrinter>,那並不意味着您必須針對每個string都有不同的實例,其中幾個實例可以共享相同的實例。

如果您不想這樣做,您可以將工廠存儲在字典中。工廠可以是一個實現接口的對象(如IApplicationPrinterFactory),或者只是一個可以創建對象的委託。在你的情況下,這將是Dictionary<string, Func<IApplicationPrinter>>。這樣做是完全類型安全的。要添加到字典中,你會做這樣的事情:如果你確定要Dictionary<string, Type>

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

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

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

,有沒有辦法來限制,使那裏所有類型都實現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

我會做略有不同:根據您的需要

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,因此它的類型也更強。

+0

我一直在尋找這個解決方案!感謝Pondidum,不再需要一個巨大的if()new A(); else if()new B(); else if()new C();否則if()new ...'在我的代碼中。 – 2014-08-14 10:34:48

0

首先,您需要更改訪問擴展數組的方法,因爲您無法在不創建實例的情況下訪問您的屬性。我會使用自定義的attribute來告訴你的類型字典每個類支持哪種擴展。這看起來像這樣:

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

使用該屬性可以限制類型。有兩種方法可以將其歸檔。

  • 扔在類的構造函數一個例外(見this link
  • 使用抽象類,而不是一個接口,這可以讓你限制你的類使用受保護的屬性(在這一點上,你需要創建這個屬性的基類可以從你的類型字典中訪問它),它只能被應用到devired類。

現在你可以可以只檢查一個特定的類有PrinterExtensions屬性,因而可檢索所有擴展它的屬性或調用其直接註冊所有擴展的方法。