简体   繁体   中英

Static fields of derived class not initialized before they are needed in the base class

I'm trying to create a base class that can be derived from to make custom enum classes that potentially have several fields and methods. The base class keeps a list of all defined values for each enum type and a name variable for all values. The problem I'm running into is that the static fields of deriving classes are not initializing until I call on one of them directly due to the behavior of the CLR, but they need to be initialized in order to add themselves to the list of defined values of that enum type.

For example, the collection Day.Values will be empty unless I previously did something like:

Day today = Day.MONDAY;

Is there a way to ensure the static fields in Day will be initialized if I call on Day.Values even if I don't call on one of the fields first? Or, is there a better way to achieve this functionality?

public abstract class EnumBase<T> where T : EnumBase<T>
{
    private static List<T> values = new List<T>();
    public static ReadOnlyCollection<T> Values
    {
        get
        {
            return values.AsReadOnly();
        }
    }

    public string name { get; private set; }

    protected EnumBase(string name)
    {
        this.name = name;
        values.Add((T)this);
    }

    public override string ToString()
    {
        return this.name;
    }
}

public class Day : EnumBase<Day>
{
    public static readonly Day MONDAY = new Day("monday");
    public static readonly Day TUESDAY = new Day("tuesday");
    //...

    private Day (string name) : base (name) 
    {

    }
}

I had no time to test this, but here is an example of having each enum class as a singleton. (I hope it works....) :D

I took the static out of your base class and added it to the "Day" singleton so that different extensions of the class will not accidentally share data.

Singletons can be called from anywhere and create one instance of themselves.

ie

Day.method(); // From anywhere

Day.addEnum('Wednesday');

public abstract class EnumBase<T> where T : EnumBase<T>
{
    private List<T> values = new List<T>();
    public ReadOnlyCollection<T> Values
    {
        get
        {
            return values.AsReadOnly();
        }
    }

    public string name { get; private set; }

    protected EnumBase()
    {

    }

    public string addEnum()
    {
        this.name = name;
        values.Add((T)this);
    }

    public override string ToString()
    {
        return this.name;
    }
}

using System;

public class Day : EnumBase<Day>
{
    private static Day instance;
    private Day() {}

    public static Day Instance
    {
        get 
        {
            if (instance == null)
            {
                instance = new Day();
            }
            return instance;
        }
    }
    addEnum('monday');
    addEnum('tuesday');
}

You could solve this using reflection to inspect the derived type for relevant fields.

Further, you can perform this initialization when Values is first accessed, deferring this work until it's needed and caching the result.

Here is an example:

public abstract class EnumBase<T> where T : EnumBase<T>
{
    static Lazy<List<T>> values = new Lazy<List<T>>(FindEnumMembers);
    public static IEnumerable<T> Values
    {
        get
        {
            return values.Value;
        }
    }

    static List<T> FindEnumMembers()
    {
        Type derivedType = typeof(T);
        return derivedType.GetFields()
                          .Where(f => f.FieldType == derivedType)
                          .Select(f => (T)f.GetValue(null))
                          .ToList();
    }

    public string name { get; private set; }

    protected EnumBase(string name)
    {
        this.name = name;
    }

    public override string ToString()
    {
        return this.name;
    }
}

public class Day : EnumBase<Day>
{
    public static readonly Day MONDAY = new Day("monday");
    public static readonly Day TUESDAY = new Day("tuesday");
    //...

    private Day (string name) : base (name) 
    {

    }
}

Access should work either way, since these are synonymous:

Day.Values
//or//
EnumBase<Day>.Values

Note again that these are synonymous, because this is the root cause of your original problem. Calling Day.Values is actually interpreted as EnumBase<Day>.Values , and thus your static members of another type Day are not initialized by this call. By using reflection in EnumBase<T> and asking for the fields of the derived type, they will be initialized (if they have not yet been) as a side-effect of calling GetValue just like they would be if you had accessed them at that point statically.

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