简体   繁体   English

如何在C#中安全地将静态类转换为实例化类

[英]How can I safely convert a static class to an instantiated class in C#

I have recently (a couple of months ago) changed jobs and have inherited a codebase which violates every single one of the SOLID principles as many times as it possibly can. 我最近(几个月前)改变了工作,继承了一个代码库,它违反了SOLID原则的每一个,尽可能多次。 It seems as though the people who wrote this code decided to study every single good coding practice in detail and violate them all as often and most radically as they possibly could. 似乎编写此代码的人决定详细研究每一个良好的编码实践,并尽可能经常地和最根本地违反它们。

I am the sole developer of this product - there is no one left in the organisation who knows the code and the codebase is too large and complex to completely rewrite. 我是该产品的唯一开发人员 - 组织中没有人知道代码,代码库太大而且复杂,无法完全重写。 I am looking at the highest value changes that I can make to make the codebase flexible and robust. 我正在研究可以使代码库变得灵活和健壮的最高价值变化。 It is not an option to abandon this product either. 放弃此产品也不是一种选择。

The root of all problems in the product stems from a group of classes which are the core business logic data structures. 产品中所有问题的根源于一组类,这些类是核心业务逻辑数据结构。 There is a lot of problems with these classes, but what I am really interested in is the following: 这些类有很多问题,但我真正感兴趣的是以下内容:

public static class NetCollection
{
    private static Logger LogFile { get { return Logger.GetMethodLogger(2); } }
    // Declare local variables.
    private static Dictionary<string, NetObject> netObjectHashTable;
    private static Dictionary<string, NetTitle> titlePropertyHashTable;
    private static Dictionary<string, NetObject> referenceDataHashTable;
    private static Dictionary<int, SortedDictionary<string, int>> picklistHashTable;

    public static IEnumerable<NetObject> NetObjects
    {
        get
        {
            return netObjectHashTable.Values;
        }
    }

    static NetCollection()
    {
        netObjectHashTable = new Dictionary<string, NetObject>();
        titlePropertyHashTable = new Dictionary<string, NetTitle>();
        referenceDataHashTable = new Dictionary<string, NetObject>();
        picklistHashTable = new Dictionary<int, SortedDictionary<string, int>>();
    }

    public static void AddNetObject(NetObject newObject)
    {
        if (newObject == null)
            return;
        if (newObject.TitleType == "Reference Data")
        {
            // Check if hash table contains key
            if (!referenceDataHashTable.ContainsKey(newObject.ID.ToString()))
            {
                referenceDataHashTable.Add(newObject.ID.ToString(), newObject);
            }
        }
        else
        {
            // Check if hash table contains key
            if (!netObjectHashTable.ContainsKey(newObject.ID.ToString()))
            {
                netObjectHashTable.Add(newObject.ID.ToString(), newObject);
            }
        }
    }
}

I have snipped quite a number of other methods from this class for the sake of brevity. 为了简洁起见,我已经从这个类中剪切了很多其他方法。

As you can see, there are a huge number of issues around this class (storing state in a static class is a huge code smell - writing your entire application around said class is just crazy). 正如你所看到的,这个类周围存在大量问题(在静态类中存储状态是一个巨大的代码味道 - 围绕所述类编写整个应用程序只是疯了)。

My current intention is to refactor this class into a proper singleton class (and eventually into a regular class so that I can enable the user to open multiple documents simultaneously). 我目前的意图是将这个类重构为一个合适的单例类(并最终进入一个常规类,这样我就可以让用户同时打开多个文档)。

Should I do this? 我应该这样做吗?

What are the biggest risks with making this change? 进行此项更改的最大风险是什么? Are there any approaches that I can take to mitigate the risk of making this change? 我是否可以采取任何方法来降低进行此更改的风险?

Yes, converting to a singleton seems like a good first step. 是的,转换为单身似乎是一个良好的第一步。 It still won't be thread-safe, but it's a start. 它仍然不是线程安全的,但它是一个开始。 You can then change it from being a true singleton to one which allows separate instances to be constructed, but also has a "default" instance in the same way as a singleton. 然后,您可以将其从真正的单例更改为允许构造单独实例的单例,但也可以像单例一样使用“默认”实例。 (You could separate the "instance holder" into a separate class, of course.) That will allow you to start writing testable code which starts with a fresh instance each time. (当然,你可以将“实例持有者”分成一个单独的类。)这将允许你开始编写可测试代码,每次都以一个新实例开始。

After that, you can start introducing dependency injection so that each class that needs access to the same instance gets it... and remove the "default" instance. 之后,您可以开始引入依赖注入,以便需要访问同一实例的每个类获取它...并删除“默认”实例。

Of course if you can reduce the number of classes which need access to the same instance, that would be better too. 当然,如果你可以减少需要访问同一个实例的类的数量,那也会更好。

For threading, you'll either need to lock in each method, or use ConcurrentDictionary . 对于线程,您需要锁定每个方法,或使用ConcurrentDictionary

If you don't know anything about how this type flows in your application, it's a dangerous task. 如果您对应用程序中此类型的流程一无所知,那将是一项危险的任务。 But if you really need to do that without, possibly, breaking everything, I would do like: 但如果你真的需要这样做,可能会打破一切,我会这样做:

Knowing that what I need is a distinct devision between documents, and I know (it's proved by time) that this type works for a single document, let's add Document slice. 知道我需要的是文档之间的明显划分,我知道(这已经证明了时间)这种类型适用于单个文档,让我们添加Document slice。

Let's assume Document has Name property, we can think about something like (example): 假设DocumentName属性,我们可以考虑像(例子)这样的东西:

public static void AddNetObject(string documentName, NetObject newObject)
{
    ....
}

make all fields non static: 使所有字段静态:

   //NO STATIC
    ...
    private Logger LogFile { get { return Logger.GetMethodLogger(2); } }   
    private Dictionary<string, NetObject> netObjectHashTable;
    private Dictionary<string, NetTitle> titlePropertyHashTable;
    private Dictionary<string, NetObject> referenceDataHashTable;
    private Dictionary<int, SortedDictionary<string, int>> picklistHashTable;

Move them into internal 将它们移动到内部

private class NetDocument {
       public string DocumentName {get;set;} //DEFINE DOCUMENT IT RELATED TO !
       ...
       private Logger LogFile { get { return Logger.GetMethodLogger(2); } }   
       private Dictionary<string, NetObject> netObjectHashTable;
       ....

  }

So you create concrete isolation between single documents and related to them data. 因此,您可以在单个文档之间创建具体的隔离,并与它们相关。

After inside the main class yo can have: 在主类里面后你可以有:

public static class NetCollection
{
    ... 

    //Key: document name
    //Value: NetDocument data
    private Dictionary<string, NetDocument> documents = new ....
}

This is just a general idea (a sketch) , you for sure need to change it to fit your needs . 这只是一个概念(草图),您肯定需要根据自己的需要进行更改

Could you wrap this around a static Factory class? 你可以将它包装在一个静态的Factory类中吗? make everything that you think you need as a singleton, and keep certain things static as needed? 制作你认为自己需要的所有单件,并根据需要保持某些东西静止? It'll solve some of the problems and leave you with some flexibility. 它将解决一些问题,并为您提供一些灵活性。

There is no real difference between a static class and a Singleton. static类和Singleton之间没有真正的区别。 Yes, it is implemented differently, but you have the same problems (and just for the sake of not-having- static I don't think it is neccessary to act). 是的,它的实现方式不同,但你有同样的问题(只是为了没有static我不认为这是必要的行为)。

If you're able to go for a "real" instances, do it. 如果你能够找到“真实”的实例,那就去做吧。 But just refactoring to Singleton because Lint doesn't complain is not the way to go IMHO. 但只是重构单身,因为林特不抱怨不是恕我直言的方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM