简体   繁体   中英

How to check if a static constructor has been called?

I have some classes that caches data from a database, these classes are loaded with data when their static constructor gets called.

I need to call a static Reload method at all these classes, except those that is not initialized yet.

Eg: City caches data from a database

public class City
{
    public City Get(string key)
    {
        City city;
        FCities.TryGetValue(key, out city);
        return city;
    }
    private static Dictionary<string, City> FCities;

    static City()
    {
        LoadAllCitiesFromDatabase();
    }

    public static void Reload()
    {
        LoadAllCitiesFromDatabase();
    }

    private static void LoadAllCitiesFromDatabase()
    {
        // Reading all citynames from database (a very slow operation)
        Dictionary<string, City> loadedCities = new Dictionary<string, City>();
        ...
        FCities = loadedCities;
    }
}

The problem is that City might not have been used yet (it might not be used in this service) and there is no reason to load it from the database then.

My reload all method looks much like this:

public static class ReloadAll
{
    public static void Do()
    {
        foreach (Type classType in AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => t.IsClass && !t.IsAbstract))
        {
            MethodInfo staticReload = classType.GetMethods().FirstOrDefault(m => m.IsStatic && m.IsPublic && m.ReturnType == typeof(void) && m.Name == "Reload" && m.GetParameters().Length == 0);
            if (staticReload != null)
            {
                if (StaticConstructorHasBeenCalled(classType))
                    staticReload.Invoke(null, null);
            }
        }
    }

    private bool StaticConstructorHasBeenCalled(Type classType)
    {
        // How do I check if static constructor has been called?
        return true;   
    }
}

I need a little help with implementing StaticConstructorHasBeenCalled.

At first glance, I thought this could be an issue where <grin> Quantum Mechanical Copenhagen interpretation might apply (" As soon as you look at it, it changes "). </grin> Ie anything you do in the class to observe whether it has been initialized would probably cause it to initialize itself...

But you don't have to do it in the class, just keep a list somewhere else (other than in any of these static classes) that is populated by every static class when it gets initialized. Then in your reset function, just iterate through the classes in the list.

If you have several of these classes, how about controlling their instantiation through a factory or manager class. This could keep track of which have been used and call the reload methods where appropriate.

You should not use long-running operations in the static constructor, or at least, they should not run synchronously. Static constructor run implicitly, and when implicit execution takes significant time, it makes for interesting bugs :)

Also, if you do use static constructors, methods and whatnot that maintain state, try to isolate them in a fully static class, so they will, for most scenarios, act like singletons. I would change the implementation to something along these lines:

public static class CityRepository
{
   private static bool areCitiesLoaded;
   private List<City> cities;

   static CityRepository()
   {
     areCitiesLoaded = false;
     cities = new List<City>();
   }

   //method that will be called in all other method, to ensure
   //that the cities are loaded
   private static void EnsureLoad()
   {
      if (!areCitiesLoaded)
      {
        LoadAllCitiesFromDatabase();
        areCitiesLoaded = true;
      }
   }
}

public class City {} //city instance methods

I asked if there was any way to see if a static constructor was called. I think that the answer was no, but a workaround would be to create a manager that could keep track of repositories. The goal was to change as little as possible to the existing classes.

My solution to a manager class is:

public static class RepositoryManager
{
    public delegate void Reload();
    private static List<Reload> FRepositories = new List<Reload>();

    public static void Register(Reload repository)
    {
        lock (FRepositories)
        {
            FRepositories.Add(repository);
        }

        repository();
    }

    public static void ReloadAll()
    {
        List<Reload> list;
        lock (FRepositories)
        {
            list = new List<Reload>(FRepositories);
        }

        foreach (Reload repository in list)
            repository();
    }
}

Using the example with the City class the changes would be limited to the static constructor.

public class City
{
    // ...

    static City()
    {
        RepositoryManager.Register(LoadAllCitiesFromDatabase);
    }

    // ...
}

My ReloadAll method would then be as simple as:

public void ReloadAll()
{
    RepositoryManager.ReloadAll();
}

Thank you for all your answers, I have rewarded each of you that suggested some kind of a manager as a solution to the problem.

The drawback of this solution is that whenever someone creates a repository that needs to be reloaded/updated/cleared once in a while they have to remember to use the RepositoryManager.

you could use the singleton pattern, and add a field that will tell you if the unique instance has already been created

actually no need to make a singleton, just keep your static class, and load the data when the property getter that should return it is called:

static class City
{
   static bool _loaded = false;

   public bool Loaded { get { return _loaded; } }

   public static List<C> Data
   {
      get
      {
         if (!_loaded)
         {
             doLoading();
             _loaded = true
         }
      }
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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