0

How can I make the behavior of a static method dependent on the class it's called from? E.g.

abstract class Base
{
    public static string GetClassName()
    { ???? }
}
class A : Base { }
class B : Base { }

static void Main(string[] args)
{
    Console.WriteLine(A.GetClassName()); // Want to print "A"
    Console.WriteLine(B.GetClassName()); // Want to print "B"
}

Note that if I use MethodBase.GetCurrentMethod().DeclaringType.Name in Base.GetClassName, then "Base" is returned.

Also note that this is similar to several other SO questions, like the below, but I'm asking it in a more succinct way in order to document an answer other than "you can't":

Community
  • 1
  • 1
Matt Thomas
  • 5,279
  • 4
  • 27
  • 59
  • 1
    Possible duplicate of [C# virtual (or abstract) static methods](http://stackoverflow.com/questions/763344/c-sharp-virtual-or-abstract-static-methods) – Jason Watkins Mar 23 '16 at 19:56
  • @JasonWatkins. Thanks for marking as dup. Per http://meta.stackoverflow.com/questions/314210/should-i-ask-a-new-question-in-order-to-make-the-answer-easier-to-find I should have done that – Matt Thomas Mar 23 '16 at 20:24

1 Answers1

1

The key is to add a type parameter:

abstract class Base<T>
    where T : Base<T>
{
    public static string GetClassName()
    {
        return typeof(T).Name;
    }
}

class A : Base<A> { }

class B : Base<B> { }

This makes static members of Base belong to the particular class Base<T>, whatever T is. So there's no longer a single Base.GetClassName method, but in this case there are now two methods: Base<A>.GetClassName and Base<B>.GetClassName. Additionally, the constraint on the type parameter makes it impossible to have class B : Base<A>, which guarantees that A.GetClassName and B.GetClassName will return different things.

Behavior specific to child classes can be extended even further:

abstract class Base<T>
where T : Base<T>
{
    private static HashSet<string> values = new HashSet<string>();

    internal Base(string value)
    {
        if (Base<T>.values.Contains(value))
            throw new Exception("Not unique");
        else
            Base<T>.values.Add(value);
    }

    public static string GetClassName()
    {
        return typeof(T).Name;
    }

    public static IEnumerable<string> GetValues()
    {
        return new LinkedList<string>(Base<T>.values);
    }
}

class A : Base<A>
{
    public A(string value) : base(value) { }
}

class B : Base<B>
{
    public B(string value) : base(value) { }
}

static void Main(string[] args)
{
    var a1 = new A("value");
    var a2 = new A("value 2");
    // var a3 = new A("value"); // Would throw an exception
    var b = new B("value"); // Does not throw an exception

    Console.WriteLine(A.GetClassName()); // Prints "A"
    Console.WriteLine(B.GetClassName()); // Prints "B"

    Console.WriteLine("The values in A:");
    foreach (var value in A.GetValues()) // This loop prints "value" and "value 2"
    {
        Console.WriteLine("\t" + value);
    }
    Console.WriteLine("The values in B:");
    foreach (var value in B.GetValues()) // This loop prints "value"
    {
        Console.WriteLine("\t" + value);
    }
}

In this case, there are different static values objects in Base<A>/A and Base<B>/B.

All this comes with a cost, however. In the above example it's impossible for child classes of Base to access the same static objects without all the child classes knowing of each other a priori. E.g. B cannot access the same values object as A. One possible workaround is to use Base<agreed-upon-type>.values when you want all child classes to share values.

Matt Thomas
  • 5,279
  • 4
  • 27
  • 59