Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Alternative to virtual static methods and static sub classes

Whenever I read questions RE this, or a similar topic of static inheritance, the replies are usually that this is not supported (we know that), and the reason is given as being because this is a poor design and there's probably a better way to do it. I'd love to find a better way of doing it so am open to all suggestions - here's what I am trying to do.

I have a class which has no instance data. All the methods are static. Let's call this class BaseStatic. I now want a new static class (well several of course but lets stick to one) which inherits from this static class and adds some new static methods, let's call this SubStatic.

What I want consumers to be able to write is:

SubStatic.MethodFromSub();

and also

SubStatic.MethodFromBase();

I know I could also write:

BaseStatic.MethodFromBase()

explicitly but then consumers have to know which class implements which methods. I can't do this with inheritance because I can't inherit one static class from another. So what's a better way of doing it?

Now, I know I can have these classes as instance classes, and I can define all the methods as static - that will give me the behaviour I described above but leads to other problems, namely:

  1. When I do this:SubStatic.MethodFromBase() the SubStatic static constructor is not called because the method is running in the parent static class (the parent's static constructor is called)

  2. If one of the static parent methods needs to call another method which the sub class can override, I need a virtual static method in the sub class. Which I know I can't have.

So poor design apparently - can anyone help me redo it? I know I can use instance inheritance and take proper use of virtual methods (I've had it working this way) but client code then always has to create an instance (or I suppose some singleton).

like image 651
RBrowning99 Avatar asked Oct 22 '12 05:10

RBrowning99


2 Answers

This could serve your purpose, though I certainly would include some exception handling and accompany its implementation with a great deal of documentation as to why and how it works.

When the static constructor for Base is run (once) all assemblies that are currently loaded in the app domain are catalogued, selecting the types that derive from Base. Iterating over those, we run the static constructors. It is worth noting though, that this no longer guarantees the cctor for each implementation will be run exactly once, logic would have to be added to each of them to re-make that assertion. Moreover, types that are loaded after the cctor for Base has been run would not be initialized by calls to methods in Base

To simulate virtual methods, use the new keyword to hide the base method. You can call the base method by qualifying it with the declaring class's name (like in class B in the example)

using System;
using System.Linq;
using System.Runtime.CompilerServices;

namespace ConsoleApplication6
{
    public class Base
    {
        static Base()
        {
            Console.WriteLine("Base cctor");

            var thisType = typeof (Base);
            var loadedTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes());
            var derivations = loadedTypes.Where(thisType.IsAssignableFrom);

            foreach(var derivation in derivations)
            {
                RuntimeHelpers.RunClassConstructor(derivation.TypeHandle);
            }
        }

        public static void Foo()
        {
            Console.WriteLine("Bar");
        }
    }

    public class A : Base
    {
        static A()
        {
            Console.WriteLine("A cctor");
        }
    }

    public class B : Base
    {
        static B()
        {
            Console.WriteLine("B cctor");
        }

        public new static void Foo()
        {
            Console.WriteLine("Bar!!");
            Base.Foo();
        }
    }

    class Program
    {
        static void Main()
        {
            Console.WriteLine("A:");
            A.Foo();
            Console.WriteLine();
            Console.WriteLine("B:");
            B.Foo();
            Console.WriteLine();
            Console.WriteLine("Base:");
            Base.Foo();
            Console.ReadLine();
        }
    }
}

EDIT

Another option lies in the CRTP (or CRGP in the C# paradigm) or curiously recurring template (generic) parameter pattern

using System;
using System.Runtime.CompilerServices;

namespace ConsoleApplication6
{
    public class Base<T>
        where T : Base<T>
    {
        static Base()
        {
            RuntimeHelpers.RunClassConstructor(typeof (T).TypeHandle);
        }

        public static void Foo()
        {
            Console.WriteLine("Bar");
        }
    }

    public class Base : Base<Base>
    {
    }

    public class A : Base<A>
    {
        static A()
        {
            Console.WriteLine("A cctor");
        }
    }

    public class B : Base<B>
    {
        static B()
        {
            Console.WriteLine("B cctor");
        }

        public new static void Foo()
        {
            Console.WriteLine("Bar!!");
            Base<B>.Foo();
        }
    }

    class Program
    {
        static void Main()
        {
            Console.WriteLine("A:");
            A.Foo();
            Console.WriteLine();
            Console.WriteLine("B:");
            B.Foo();
            Console.WriteLine();
            Console.WriteLine("Base:");
            Base.Foo();
            Console.ReadLine();
        }
    }
}

In this case, when we call a static method on A we're really calling it on Base<A> which is different than Base<B> or Base so we can actually determine how the method was called and run the appropriate cctor.

like image 87
mlorbetske Avatar answered Nov 15 '22 12:11

mlorbetske


You can achieve this by using Generics. For example you can use something like that:

public class MainStatic<T> where T : MainStatic<T>
{
    public static void Foo()
    {
    }

    static MainStatic()
    {
        RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
    }
}

public class SubStatic : MainStatic<SubStatic>
{
    public static void Bar()
    {
    }
}

public class Instance
{
    public void FooBar()
    {
        SubStatic.Foo();
        SubStatic.Bar();
    }
}
like image 44
Guillaume Avatar answered Nov 15 '22 13:11

Guillaume