This question has been asked dozens of times, and usually the answer is no, ranging from "you cannot" to "you should not", or "it does not make sense." Many capable developers here on SO and on various blogs, etc., Went a lot to explain why this feature is not suitable for C #.
But the bottom line has valid use cases for static interfaces - I often use them in PHP, and of course there is a way to achieve something similar in C #. You do not need to think or hack, you just need to tweak your thinking a little.
First of all, think what exactly means “static”:
public static class Foo { static Foo() { Value = "test"; } public static string Value { get; set; } }
In a sense, the Foo class is just a global singleton object with the Bar property. This object is automatically created for you at startup, and you cannot create more than one instance, but otherwise it is functionally equivalent to the following non-stationary class:
public class Bar { public Bar() { Value = "test"; } public string Value { get; set; } }
It is necessary to use a different syntax for the constructor, and access to properties and method calls look different, but if you decide to save only one global instance of this object, and you decide to create this instance at startup, functionally, there is no difference.
The goal of this is to prepare you for the next idea: your types do not have to be static.
The reason you need an interface function is because you need to specify a “contract” to which your types should correspond, but interfaces do not work for you, because they determine how objects should correspond.
My answer to this question is simply to implement your types as concrete objects and store them statically - instead of relying on a “static” keyword for the part of your type that must match the interface.
For example, consider the Animal type with the Cat and Dog subtypes - and assume that all animals of a certain type produce the same sound, say that our animal types must provide sound. As you already know, the following does not work:
public abstract class Animal { public static abstract string Sound { get; } } public class Cat : Animal { public static string Sound { get { return "Mee-oww."; } } } public class Dog : Animal { public static string Sound { get { return "Woof!"; } } } public void Test() { Animal cat = new Cat(); Animal dog = new Dog(); Assert.AreEqual(cat.GetType().Sound, Cat.Sound); Assert.AreEqual(dog.GetType().Sound, Dog.Sound); }
Besides the fact that static abstract not supported, another serious problem with the test is that cat.GetType() returns a System.Type , which is a reflection of the type definition itself, and not a reference to a global static object that will be automatically created at startup. Since there is no syntax for abstract static methods, it follows that there is also no syntax for statically invoking an implementation of such a method.
The only way to access the static sound property is to directly refer to the type, for example. Cat.Sound or Dog.Sound . Okay, so this is not entirely true - you can access methods using reflection, but it probably won't be very convenient. Of course, you can also add another non-static property in each type of Animal for each property of the parent class, which explicitly refers to static properties. Again, I don't think this is a very convenient approach if you have many kinds of animals ...
Start over.
Forget about trying to use static properties and standard typing to achieve what you want - instead, add a specific type that defines what we define as an animal type:
public class AnimalType { public AnimalType(string sound) { Sound = sound; } public string Sound { get; private set; } }
Since System.GetType() only works for system types, we need a similar tool for Animal types, and we will leave this unrealized - forcing each specific Animal type to ensure that:
public abstract class Animal { public abstract AnimalType AnimalType { get; } }
Now we can implement specific types of animals - because we want one AnimalType to accompany each system type that extends the Animal class, we will define and store an instance of AnimalType in a static field inside each type - and our implementation of the AnimalType property will return this static instance:
public class Dog : Animal { public static readonly AnimalType Type = new AnimalType(sound: "Woof!"); override public AnimalType AnimalType { get { return Type; } } } public class Cat : Animal { public static readonly AnimalType Type = new AnimalType(sound: "Mee-oww."); override public AnimalType AnimalType { get { return Type; } } }
Now we can write a working test:
public void StaticMethodInterface() { Animal dog = new Dog(); Animal cat = new Cat(); Assert.AreEqual(dog.AnimalType.Sound, Dog.Type.Sound); Assert.AreEqual(cat.AnimalType.Sound, Cat.Type.Sound); }
The missing part is a tool that allows us to work with Animal types when all we have is System.Type but not the actual instance. In other words, we know the type of Animal, and we need access to it AnimalType. I came up with several solutions for this, some of which are related to reflection, some of which require an empty constructor in animal types. My favorite solution is to add a simple registry that maps to each Animal system type corresponding to AnimalType:
public class AnimalType { public AnimalType(string sound) { Sound = sound; } public string Sound { get; private set; } private static IDictionary<Type, AnimalType> _types = new Dictionary<Type, AnimalType>(); public static void Register(Type type, AnimalType animalType) { _types.Add(type, animalType); } public static AnimalType Get(Type type) { return _types[type]; } }
I believe that the safest way to populate this registry is to add static constructors to each system type of Animal, for example:
public class Dog : Animal { public static readonly AnimalType Type = new AnimalType(sound: "Woof!"); static Dog() { AnimalType.Register(typeof(Dog), Type); } override public AnimalType AnimalType { get { return Type; } } } public class Cat : Animal { public static readonly AnimalType Type = new AnimalType(sound: "Mee-oww."); static Cat() { AnimalType.Register(typeof(Cat), Type); } override public AnimalType AnimalType { get { return Type; } } }
This requires a little discipline at your end, like anything related to tasks related to runtime. I believe that this extra bit of work is preferable to using reflection or empty constructors to populate the registry.
Finally, we can add a test that demonstrates how to use System.Type to get AnimalType if we don't have an instance:
public void Test() { var dogType = typeof (Dog); var catType = typeof (Cat); Assert.AreEqual(Dog.Type.Sound, AnimalType.Get(dogType).Sound); Assert.AreEqual(Cat.Type.Sound, AnimalType.Get(catType).Sound); }
Finally, here is a complete example and test:
public class AnimalType { public AnimalType(string sound) { Sound = sound; } public string Sound { get; private set; } private static IDictionary<Type, AnimalType> _types = new Dictionary<Type, AnimalType>(); public static void Register(Type type, AnimalType animalType) { _types.Add(type, animalType); } public static AnimalType Get(Type type) { return _types[type]; } } public abstract class Animal { public abstract AnimalType AnimalType { get; } } public class Dog : Animal { public static readonly AnimalType Type = new AnimalType(sound: "Woof!"); static Dog() { AnimalType.Register(typeof(Dog), Type); } override public AnimalType AnimalType { get { return Type; } } } public class Cat : Animal { public static readonly AnimalType Type = new AnimalType(sound: "Mee-oww."); static Cat() { AnimalType.Register(typeof(Cat), Type); } override public AnimalType AnimalType { get { return Type; } } } public void Test() { Animal dog = new Dog(); Animal cat = new Cat(); Assert.AreEqual(dog.AnimalType.Sound, Dog.Type.Sound); Assert.AreEqual(cat.AnimalType.Sound, Cat.Type.Sound); var dogType = typeof (Dog); var catType = typeof (Cat); Assert.AreEqual(Dog.Type.Sound, AnimalType.Get(dogType).Sound); Assert.AreEqual(Cat.Type.Sound, AnimalType.Get(catType).Sound); }
Note that in this simple step-by-step tutorial, I use one AnimalType and register an instance for each system type Animal. Suppose you need alternative implementations for different types of Animal - just declare AnimalType abstract (or turn it into an interface), and then add it to the specific CatType and DogType types by registering them.
Finally, take a moment to reflect on the fact that it is actually much more flexible than static interfaces - in a sense, you define your own meta type definition on your domain. The advantage is that you can freely model your meta types using inheritance and interfaces, just like you model any other type hierarchy.
I don't consider this a workaround - in a sense, static interfaces will simply be the new syntax for something that you can already do better. By modeling your meta-types in the form of specific system types, you get a more user-friendly code base that will scale to meet new requirements without reprofiling in harsh conditions; you just extend your meta types just like other types.
Just adding static interfaces will not give you that degree of flexibility.