繁体   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