Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# static function in interface

Tags:

c#

directx

Is there a way to simulate a static function in an interface in C#?

I want to use it for a factory, in which every object inherits from ICreateAble with a static function 'Create' and in the factory class you can then call Factory.Create(Type t, string fromFile) which calls t.Create()

like image 512
Alex Kruger Avatar asked Mar 07 '11 15:03

Alex Kruger


People also ask

Bahasa C digunakan untuk apa?

Meskipun C dibuat untuk memprogram sistem dan jaringan komputer namun bahasa ini juga sering digunakan dalam mengembangkan software aplikasi. C juga banyak dipakai oleh berbagai jenis platform sistem operasi dan arsitektur komputer, bahkan terdapat beberepa compiler yang sangat populer telah tersedia.

C dalam Latin berapa?

C adalah huruf ketiga dalam alfabet Latin. Dalam bahasa Indonesia, huruf ini disebut ce (dibaca [tʃe]).

Bahasa C dibuat pertama kali oleh siapa dan tahun berapa?

Bahasa pemrograman C ini dikembangkan antara tahun 1969 – 1972 oleh Dennis Ritchie. Yang kemudian dipakai untuk menulis ulang sistem operasi UNIX. Selain untuk mengembangkan UNIX, bahasa C juga dirilis sebagai bahasa pemrograman umum.


1 Answers

This question has been asked dozens of times, and usually the answer is in the negative, ranging from "you can't" to "you shouldn't", or "it doesn't make sense". Many capable developers here on SO and various blogs etc. have gone to great lengths to explain why this feature is not a good fit for the C# language.

But the bottom line is, there are valid use-cases for static interfaces - I use them frequently in PHP, and there is of course a way to achieve something similar in C#. You don't need reflection or hacks, you just need to adjust your thinking a little bit.

First off, consider what exactly "static" means:

public static class Foo
{
    static Foo()
    {
        Value = "test";
    }

    public static string Value { get; set; }
}

In a sense, class Foo is just a global singleton object with a Bar property. This object gets created for you automatically, at startup, and you cannot create more than one instance - but it is otherwise functionally equivalent to the following non-static class:

public class Bar
{
    public Bar()
    {
        Value = "test";
    }

    public string Value { get; set; }
}

You need to use a different syntax for the constructor, and property access and method invocation looks different - but if you choose to keep only one global instance of this object, and you choose to create that instance at startup, functionally, there is no difference.

The point of this, is to prepare you for the following idea: your types don't need to be static.

The reason why you want the interface feature, is because you have a need to specify a "contract" to which your types need to conform - but interfaces don't work for you, because they specify how the objects need to conform.

My answer to this, is to simply implement your types as concrete objects and store them statically - rather than relying on the "static" keyword for the portion of your type that needs to conform to an interface.

For example, let's consider a type Animal, with subtypes Cat and Dog - and assuming that all animals of a particular type make the same sound, let's say that our animal-types must provide a 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 is 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 the global static object that would be auto-created at startup. Since there is no syntax for abstract static methods, it follows that there is also no syntax for statically calling an implementation of such a method.

The only way to access the static Sound property, is by referencing the type directly, e.g. Cat.Sound or Dog.Sound. Okay, so that's not entirely true - you could access the methods using reflection, but it probably wouldn't be very convenient. Granted, you could also add another non-static property in every Animal-type, for every property in the parent class, that accesses the static properties explicitly. Again, I don't think that makes for a very maintainable approach if you have lots of Animal-types...

Let's start over.

Forget trying to use static properties and standard typing to achieve what you want - instead, let's add a concrete type that specifies 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 facility for Animal-types, and we'll leave that unimplemented - forcing each concrete Animal-type to provide that:

public abstract class Animal
{
    public abstract AnimalType AnimalType { get; }
}

Now we can implement concrete types of animals - since we want one AnimalType to accompany every system-type that extends the Animal class, we will define and store the AnimalType instance in a static field inside each type - and our implementation of the AnimalType-property will return that 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; }
    }
}

We can now 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 piece, is a facility that enables us to work with Animal-types when all we have is a System.Type, but no actual instance. In other words, we know the type of Animal, and we need access to it's AnimalType. I came up with several solutions to this, some involving reflection, some requiring an empty constructor in Animal-types. My favorite solution is to add a simple registry that maps each Animal system-type to it's matching 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 find that the safest way to populate this registry, is to add static constructors to each Animal system-type, like so:

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 does require a little discipline on your end, as does anything involving run-time type-related tasks. I find this extra bit of work is preferable to using reflection or empty constructors to populate the registry.

Finally, we can add a test, demonstrating how to use a System.Type to obtain the AnimalType, when 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's the 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 walkthrough, I'm using a single AnimalType, and registering an instance for every Animal system-type. Suppose you need alternative implementations for different Animal-types - simply declare AnimalType as abstract (or turn it into an interface), and then extend it into concrete CatType and DogType types, registering those instead.

Finally, take a moment to ponder the fact that this is actually a lot more flexible than static interfaces would have been - in a sense, you're definining your own domain-specific definition of a "meta-type". The benefit is that you can freely model your meta-types using inheritance and interfaces, the same way you model any other type-hierarchy.

I don't consider this a work-around - in a sense, static interfaces would just be new syntax for something you can already do better. By modeling your meta-types as concrete system-types, you get a more maintainable codebase that will scale up to meet new requirements without heavy-duty refactoring; you simply extend your meta-types the same way you extend any other type.

Simply adding static interfaces would not give you that degree of flexibility.

like image 62
mindplay.dk Avatar answered Oct 17 '22 04:10

mindplay.dk