Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to define a static property which is defined once per sub-class?

I wrote the following console app to test static properties:

using System;

namespace StaticPropertyTest
{
    public abstract class BaseClass
    {
        public static int MyProperty { get; set; }
    }

    public class DerivedAlpha : BaseClass
    {
    }

    public class DerivedBeta : BaseClass
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            DerivedBeta.MyProperty = 7;
            Console.WriteLine(DerivedAlpha.MyProperty); // outputs 7
        }
    }
}

As this console app demonstrates, the MyProperty property exists once for all instances of BaseClass. Is there a pattern to use which would allow me to define a static property which will have allocated storage for each sub-class type?

Given the above example, I would like all instances of DerivedAlpha to share the same static property, and all instances of DerivedBeta to share another instance of the static property.

Why am I trying to do this?

I am lazily initializing a collection of class property names with certain attributes (via reflection). The property names will be identical for each derived class instance, so it seems wasteful to store this in each class instance. I can't make it static in the base class, because different sub-classes will have different properties.

I don't want to replicate the code which populates the collection (via reflection) in each derived class. I know that one possible solution is to define the method to populate the collection in the base class, and call it from each derived class, but this is not the most elegant solution.

Update - Example of what I'm doing

At Jon's request, here's an example of what I'm trying to do. Basically, I can optionally decorate properties in my classes with the [SalesRelationship(SalesRelationshipRule.DoNotInclude)] attribute (there are other attributes, this is just a simplified example).

public class BaseEntity
{
    // I want this property to be static but exist once per derived class.
    public List<string> PropertiesWithDoNotInclude { get; set; }

    public BaseEntity()
    {
        // Code here will populate PropertiesWithDoNotInclude with
        // all properties in class marked with
        // SalesRelationshipRule.DoNotInclude.
        //
        // I want this code to populate this property to run once per
        // derived class type, and be stored statically but per class type.
    }
}

public class FooEntity : BaseEntity
{
   [SalesRelationship(SalesRelationshipRule.DoNotInclude)]
   public int? Property_A { get; set; }

   public int? Property_B { get; set; }

   [SalesRelationship(SalesRelationshipRule.DoNotInclude)]
   public int? Property_C { get; set; }
}

public class BarEntity : BaseEntity
{
   public int? Property_D { get; set; }

   [SalesRelationship(SalesRelationshipRule.DoNotInclude)]
   public int? Property_E { get; set; }

   public int? Property_F { get; set; }
}

Desired end result

Accessing FooEntity.PropertiesWithDoNotInclude returns a List<string> of:

{
  "Property_A",
  "Property_C"
}

Accessing BarEntity.PropertiesWithDoNotInclude returns a List<string> of:

{
  "Property_E"
}
like image 755
LeopardSkinPillBoxHat Avatar asked Jul 22 '13 05:07

LeopardSkinPillBoxHat


2 Answers

Two possible approaches:

  • Use attributes; decorate each subclass with an attribute, e.g.

    [MyProperty(5)]
    public class DerivedAlpha
    {
    }
    
    [MyProperty(10)]
    public class DerivedBeta
    {
    }
    

    That only works when they're effectively constants, of course.

  • Use a dictionary:

    var properties = new Dictionary<Type, int>
    {
        { typeof(DerivedAlpha), 5) },
        { typeof(DerivedBeta), 10) },
    };
    

EDIT: Now that we have more context, Ben's answer is a really good one, using the way that generics work in C#. It's like the dictionary example, but with laziness, thread-safety and simple global access all built in.

like image 79
Jon Skeet Avatar answered Oct 19 '22 13:10

Jon Skeet


Jon has a good solution as usual, although I don't see what good attributes do here, since they have to be explicitly added to every subtype and they don't act like properties.

The Dictionary approach can definitely work. Here's another way to do that, which explicitly declares that there will be one variable per subclass of BaseEntity:

class FilteredProperties<T> where T : BaseEntity
{
     static public List<string> Values { get; private set; }
     // or static public readonly List<string> Values = new List<string>();
     static FilteredProperties()
     {
         // logic to populate the list goes here
     }
}

The drawback of this is that it's rather difficult to pair with a GetType() call such as you might use in methods of BaseEntity. A Dictionary, or wrapper thereto which implements lazy population, is better for that usage.

like image 33
Ben Voigt Avatar answered Oct 19 '22 13:10

Ben Voigt