I wrote the following console app to test static properties:
using System;
namespace StaticPropertyTest
{
public abstract class BaseClass
{
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.
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.
I just recently needed this same thing and came across this question. I think Jon's and Fried's ideas to use a Dictionary
are on the right track but don't quite hit what I was looking for so I thought I'd show my own complete and very easy to extend implementation.
public class TypeStaticProperty<T>
{
T _defaultValue;
Dictionary<Type, T> _values = new Dictionary<Type, T>();
public TypeStaticProperty(T defalutValue = default)
{
_defaultValue = defalutValue;
}
public T Get(object caller)
{
lock (_values)
{
if (_values.TryGetValue(caller?.GetType(), out T val))
return val;
else
return _defaultValue;
}
}
public void Set(object caller, T val)
{
lock (_values)
_values[caller?.GetType()] = val;
}
}
And to demonstrate:
class TestBaseClass
{
static TypeStaticProperty<int> _property = new TypeStaticProperty<int>();
public int Property
{
get => _property.Get(this);
set => _property.Set(this, value);
}
}
class TestClass1 : TestBaseClass
{
}
class TestClass2 : TestBaseClass
{
}
class Program
{
static void Main(string[] args)
{
TestClass1 test1a = new TestClass1();
TestClass1 test1b = new TestClass1();
test1a.Property = 1;
test1b.Property = 2;
TestClass2 test2a = new TestClass2();
TestClass2 test2b = new TestClass2();
test2a.Property = 3;
test2b.Property = 4;
Console.WriteLine($"test1a.Property = {test1a.Property}");
Console.WriteLine($"test1b.Property = {test1b.Property}");
Console.WriteLine($"test2a.Property = {test2a.Property}");
Console.WriteLine($"test2b.Property = {test2b.Property}");
}
}
Output:
test1a.Property = 2
test1b.Property = 2
test2a.Property = 4
test2b.Property = 4
So while you still need a class instance to access and set the property, the value will always be the same across all instances of that precise type. (This includes generics too; Foo<int>
will be seen as a different type than Foo<string>
). This has the huge advantage over Fried's example in that you don't need to know at compile time the precise type whose "static" value you're looking for when accessing or setting.
PS - For full disclosure, this was heavily inspired by the WPF source code, which uses a very similar pattern for DependencyProperty
's and all kinds of other internal bells and whistles designed to improve performance and reduce memory footprint.