簡體   English   中英

如何重構當前使用字典的 C# 代碼以減少冗余並提高類型安全性?

[英]How can I refactor this C# code currently using Dictionarys to have even less redundancy and be more typesafe?

由於超出了我的工資等級的業務決策,我需要解析和合並多個 XML 文件。

為了減少冗余代碼,我有這個 map:

        private static readonly Dictionary<string, Type> listTypeByFileName = new Dictionary<string, Type> {
            {"a.xml", typeof(List<A>)},
            {"b.xml", typeof(List<B>)},
            {"c.xml", typeof(List<C>)},
            {"d.xml", typeof(List<D>)},
            // etc.
        };

因為這個 map 是如何使用的,在下載並解析所有 XML 之后,結果是Dictionary<string, object>類型,其中key與上面 map 中的 key 相同, value是 Z1D78DC8ED512144EB0AEZ8 中指定的類型,作為使用 DownloadFiles(config) 執行此代碼的結果:

        private static Dictionary<string, object> DownloadFiles(IConfigurationRoot config) {
            Dictionary<string, object> dataListByFileNames = new Dictionary<string, object>();
            listTypeByFileName.Keys.ToList()
                .ForEach(name => dataListByFileNames.Add(name, DownloadData(name, config)));
            return dataListByFileNames;
        }

        private static object DownloadData(string name, IConfigurationRoot config) {
            _ = listTypeByFileName.TryGetValue(name, out Type listType);
            return new XmlSerializer(listType, new XmlRootAttribute("Document"))
                .Deserialize(new StringReader(DownloadFromBlobStorage(name, config).ToString()));
        }

        private static CloudBlockBlob DownloadFromBlobStorage(string filetoDownload, IConfigurationRoot config) {
            return CloudStorageAccount.Parse(config["AzureWebJobsStorage"])
                .CreateCloudBlobClient()
                .GetContainerReference(config["BlobStorageContainerName"])
                .GetBlockBlobReference(filetoDownload);

第一個問題:有沒有一種方法可以使返回更加類型安全? 也許使用參數化類型?

問題的第二部分實際上是在使用這個Dictionary

對於此Dictionary中的每種類型,我現在需要一個 function ,例如:

        private void AddA(Dictionary<string, object> dataByFileNames) {
            if (dataByFileNames.TryGetValue("a.xml", out object data)) {
                List<A> aList = (List<A>)data;
                aList.ForEach(a =>
                    doSomethingWithA(a);
                );
            }
        }

        private void AddB(Dictionary<string, object> dataByFileNames) {
            if (dataByFileNames.TryGetValue("b.xml", out object data)) {
                List<B> bList = (List<B>)data;
                bList.ForEach(b =>
                    doSomethingWithB(b);
                );
            }
        }

       // etc.

因為我已經有了類型的文件名列表(這個問題的頂部),我覺得應該有一些方法來抽象上面的內容,所以它不需要一次又一次地重復。 請注意,可能很重要的是,每種類型(A、B、C、D 等)都有一個屬性string Id ,所有doStringWithX()方法都肯定需要它......如果有用,我可以創建一個接口得到這個。如果我需要在每個doStringWithX()中或在調用這些方法中的每一個時強制轉換為正確的類型,那沒關系。c

首先,不要將List<T>類型存儲在字典中,而只需存儲底層泛型類型:

private static readonly Dictionary<string, Type> listTypeByFileName = new Dictionary<string, Type> {
    {"a.xml", typeof(A)},
    {"b.xml", typeof(B)}
    // etc.

這將使未來的步驟更容易一些。 反序列化時,創建通用列表類型。 從字典中獲取類型后,您可以執行以下操作:

var listType = typeof(List<>).MakeGenericType(typeRetrievedFromDictionary);

反序列化后,將其轉換為IList 這實際上是將其轉換為object的列表。 沒關系。 因為您使用特定類型反序列化,所以列表中的每個項目都將是預期的類型。

為列表中每次要調用的類型安全方法創建一個字典。

Dictionary<Type, Action<object>> methodsToInvokeByType;

向字典中添加方法:

doSometingMethods.Add(typeof(A), dataItem => DoSomethingWithA((A)dataItem));
doSometingMethods.Add(typeof(B), dataItem => DoSomethingWithB((B)dataItem));

現在,一旦你的IList充滿了對象,你就可以檢索類型安全的方法來調用:

var methodToInvoke = methodsToInvokeByType[typeRetrievedFromDictionary];

然后這樣做:

foreach(object itemInList in list) // this is your deserialized list cast as IList
{
    methodToInvoke(itemInList);
}

因此,如果類型是A ,您將調用

DoSomethingWithA((A)itemInList)

這不漂亮。 使用對象和類型的代碼和類型安全的通用代碼之間的橋接可能會很混亂。 但最終的目標是,無論最終方法是什么—— DoSomethingWithADoSomethingWithB等,至少它們是類型安全的。


你可以再簡化一些:

創建一個 class 反序列化列表並將其傳遞給處理方法和接口:

public interface IXmlFileProcessor
{
    void Process(byte[] xmlFile);
}

public class XmlFileProcessor<T> : IXmlFileProcessor
{
    private readonly Action<T> _doSomething;

    public XmlFileProcessor(Action<T> doSomething)
    {
        _doSomething = doSomething;
    }

    public void Process(byte[] xmlFile) // or string or whatever
    {
        // deserialize into a List<T>
        foreach (T item in deserializedList)
            _doSomething(item);
    }
}

然后創建一個Dictionary<Type, IXmlFileProcessor>並填充它:

fileProcessors.Add(typeof(A), new XmlFileProcessor<A>(SomeClass.DoSomethingWithA));
fileProcessors.Add(typeof(B), new XmlFileProcessor<B>(SomeClass.DoSomethingWithB));

該方法(注入Action )旨在使“做某事”方法與負責反序列化的 class 分離。 DoSomething也可以是XmlFileProcessor<T>中的通用方法。 有多種方法可以組合這些類並將它們添加到該字典中。 但無論哪種方式,確定類型后,您只需從字典中檢索正確的特定於類型的處理器,將文件傳遞給它,它就會執行 rest。

該方法通過使 class - XmlFileProcessor<T> - 通用,但讓它實現非通用接口來彌合通用/非通用差距。 只要您采取措施(使用字典)以確保您為要反序列化的任何類型選擇正確的實現,它就可以工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM