Why can't I declare C# methods virtual and static?

Viewed 65062

I have a helper class that is just a bunch of static methods and would like to subclass the helper class. Some behavior is unique depending on the subclass so I would like to call a virtual method from the base class, but since all the methods are static I can't create a plain virtual method (need object reference in order to access virtual method).

Is there any way around this? I guess I could use a singleton.. HelperClass.Instance.HelperMethod() isn't so much worse than HelperClass.HelperMethod(). Brownie points for anyone that can point out some languages that support virtual static methods.

Edit: OK yeah I'm crazy. Google search results had me thinking I wasn't for a bit there.

16 Answers

I don't think you are crazy. You just want to use what is impossible currently in .NET.

Your request for virtual static method would have so much sense if we are talking about generics. For example my future request for CLR designers is to allow me to write intereface like this:

public interface ISumable<T>
{
  static T Add(T left, T right);
}

and use it like this:

public T Aggregate<T>(T left, T right) where T : ISumable<T>
{
  return T.Add(left, right);
}

But it's impossible right now, so I'm doing it like this:

    public static class Static<T> where T : new()
    {
      public static T Value = new T();
    }

    public interface ISumable<T>
    {
      T Add(T left, T right);
    }

    public T Aggregate<T>(T left, T right) where T : ISumable<T>, new()
    {
      return Static<T>.Value.Add(left, right);
    }

Virtual static methods don't make sense. If I call HelperClass.HelperMethod();, why would I expect some random subclass' method to be called? The solution really breaks down when you have 2 subclasses of HelperClass - which one would you use?

If you want to have overrideable static-type methods you should probably go with:

  • A singleton, if you want the same subclass to be used globally.
  • A tradition class hierarchy, with a factory or dependency injection, if you want different behavior in different parts of your application.

Choose whichever solution makes more sense in your situation.

Indeed, this can be done in Delphi. An example:

type
  TForm1 = class(TForm)
    procedure FormShow(Sender: TObject);
  end;

  TTestClass = class
  public
    class procedure TestMethod(); virtual;
  end;

  TTestDerivedClass = class(TTestClass)
  public
    class procedure TestMethod(); override;
  end;

  TTestMetaClass = class of TTestClass;

var
  Form1: TForm1;

implementation

{$R *.dfm}

class procedure TTestClass.TestMethod();
begin
  Application.MessageBox('base', 'Message');
end;

class procedure TTestDerivedClass.TestMethod();
begin
  Application.MessageBox('descendant', 'Message');
end;


procedure TForm1.FormShow(Sender: TObject);
var
  sample: TTestMetaClass;
begin
  sample := TTestClass;
  sample.TestMethod;
  sample := TTestDerivedClass;
  sample.TestMethod;
end;

Quite interesting. I no longer use Delphi, but I recall being able to very easily create different types of controls on a custom designer canvas using the metaclass feature: the control class, eg. TButton, TTextBox etc. was a parameter, and I could call the appropriate constructor using the actual metaclass argument.

Kind of the poor man's factory pattern :)

a static method exists outside of an instance of a class. It cannot use any non-static data.

a virtual method will be "overwritten" by an overloaded function depending of the type of an instance.

so you have a clear contradiction between static and virtual.

This is not a problem of support, It is a concept.

Update: I was proven wrong here(see comments):

So I doubt you will find any OOP-Language which will support virtual static methods.

I heard that Delphi suports something like this. It seems it does it by making classes object instances of a metaclass.

I've not seen it work, so I'm not sure that it works, or what's the point for that.

P.S. Please correct me if I'm wrong, since it's not my domain.

Because a virtual method uses the defined type of the instantiated object to determine which implementation to execute, (as opposed to the declared type of the reference variable)

... and static, of course, is all about not caring if there's even an instantiated instance of the class at all...

So these are incompatible.

Bottom line, is if you want to change behavior based on which subclass an instance is, then the methods should have been virtual methods on the base class, not static methods.

But, as you already have these static methods, and now need to override them, you can solve your problem by this: Add virtual instance methods to the base class that simply delegate to the static methods, and then override those virtual instance wrapper methods (not the static ones) in each derived subclass, as appropriate...

You can use the new keyword

namespace AspDotNetStorefront
{
    // This Class is need to override StudioOnlineCommonHelper Methods in a branch
    public class StudioOnlineCommonHelper : StudioOnlineCore.StudioOnlineCommonHelper
    {
        //
        public static new void DoBusinessRulesChecks(Page page)
        {
            StudioOnlineCore.StudioOnlineCommonHelper.DoBusinessRulesChecks(page);
        }
    }
}

It is possible to simulate the functionality by using the new keyword in the derived class and throwing the NotSupportedException() in the base.

public class BaseClass{
    public static string GetString(){
        throw new NotSupportedException();   // This is not possible
    }
}

public class DerivedClassA : BaseClass {
    public static new string GetString(){
        return "This is derived class A";
    }
}

public class DerivedClassB : BaseClass {
    public static new string GetString(){
        return "This is derived class B";
    }
}

static public void Main(String[] args)
{
    Console.WriteLine(DerivedClassA.GetString());  // Prints "This is derived class A"
    Console.WriteLine(DerivedClassB.GetString());  // Prints "This is derived class B"
    Console.WriteLine(BaseClass.GetString());      // Throws NotSupportedException
}

Due to the fact that it is not possible to detect this condition at compile time and that IntelliSense won't suggest that such function should be implemented in the derived class, this is a potential headache.

One comment also suggested to use NotImplemetedException(). Microsoft's documentation indicates that neither of these exceptions should be handled so any of them should work.

The differences between NotSupportedException and NotImplemetedException are commented in this blog.

Related