67

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.

Luke
  • 18,585
  • 24
  • 87
  • 110
  • 1
    This kind of scenario means you should probably turn this into objects (not singletons). – yfeldblum Oct 29 '08 at 20:38
  • 2
    I don't want to have to instantiate an object everywhere this helper method is used though. – Luke Oct 29 '08 at 20:45
  • Then use a singleton, or dependency injection, depending on which makes sense. – Chris Marasti-Georg Oct 29 '08 at 20:53
  • 1
    Possible duplicate of [Why can't I have abstract static methods in C#?](https://stackoverflow.com/questions/3284/why-cant-i-have-abstract-static-methods-in-c) – Ken Kin Jan 10 '18 at 04:57
  • Possible duplicate of [How to implement virtual static properties?](https://stackoverflow.com/questions/15346631/how-to-implement-virtual-static-properties) – peterh Jan 10 '18 at 11:13
  • In [this answer](http://stackoverflow.com/a/66070907/6053778) I describe a pattern that can emulate the desired behaviour. – Contactomorph Jun 08 '21 at 20:17

16 Answers16

47

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);
    }
NetMage
  • 26,163
  • 3
  • 34
  • 55
SeeR
  • 2,158
  • 1
  • 20
  • 35
  • @SeeR: -1: I'll correct that if I'm wrong, but this doesn't solve the original problem. – John Saunders Apr 08 '10 at 02:53
  • 3
    @John Saunders: He want static virtual methods, but it's impossible - He must use instance methods for it. He also don't want to instantiate this class everytime he want to use this static (now instance) methods - this is why I created Static class. Now he will only have only one instance of his class for the whole application. I think it's acceptable tax for such functionality. Aggregate method is just an example how he can use it. So in summary we have static virtual replacement in c# - wasn't that the request in question? – SeeR Apr 09 '10 at 08:21
  • 4
    @SeeR: Your approach works fine if the type in question satisfies a `new` constraint. Static virtual methods would allow such constructs to be used in type-safe fashion with types that didn't satisfy a `new` constraint. Alternatively, one could have the `Static` class not have a `new` constraint, but rather attempt to use Reflection to create an object with a special parameterized constructor. Unfortunately, I don't know any way to make that type-safe. – supercat Oct 31 '12 at 20:05
34

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.

Chris Marasti-Georg
  • 34,091
  • 15
  • 92
  • 137
  • 28
    -1 Because you forgot about generics. T.Add(leftT, rightT); has so much sense. Isn't it? – SeeR Mar 10 '10 at 23:32
  • 4
    And how would you override your implementation in a subclass? – Chris Marasti-Georg Mar 12 '10 at 18:03
  • It could be implemented as a virtual instance member of the class' type. Subclasses' types would inherit from the class' type, allowing them to override. – Craig Gidney Apr 08 '10 at 02:57
  • 3
    Yes, but the subclass' implementation would not be available from the base type in a static context, which is what the original question asked. – Chris Marasti-Georg Apr 13 '10 at 15:34
  • 15
    Yes, they do make sense, it's just a matter of semantics. I'd argue that if it makes sense to declare a static method inside a class, it makes sense to allow overriding it on a per-type basis. See the Delphi examples in this thread. You may argue that 'static' has a defined meaning; well, in C it has a very different meaning from C# and Java, for example. – Flávio Etrusco Aug 05 '13 at 20:50
  • 3
    @etrusco is right. Other languages, like Python, have virtual static methods. In those languages, the object is available for dynamic dispatch, but not passed to the method (since the method is static). – Neil G Jan 25 '17 at 20:06
  • Yes it DOES make sense. This should not be marked as the answer. – John Stock Jan 11 '21 at 01:44
  • This answer makes no sense and I can't understand how it has so many upvotes. Why would I expect `HelperClass.HelperMethod()` to call some random subclass' method any more than I would expect `helperInstance.HelperMethod()` to do so? It's just nonsensical. It makes perfect sense that I should be able to create a virtual static method. If it's called on the base class, it calls the base method. If it's called on the derived class, it calls the overridden method. It's precisely the same as if the methods were not static and I were making the calls using instances of the classes. – Clonkex Nov 07 '22 at 02:20
19

You can achieve the same effect by just having a regular static method and then shadow it with the new keyword

public class Base 
{
    //Other stuff

    public static void DoSomething()
    {
        Console.WriteLine("Base");
    }
}

public class SomeClass : Base
{
    public new static void DoSomething()
    {
        Console.WriteLine("SomeClass");
    }
}
public class SomeOtherClass : Base
{
}

Then you can call the methods like so

Base.DoSomething(); //Base
SomeClass.DoSomething(); //SomeClass
SomeOtherClass.DoSomething(); //Base
Davy8
  • 30,868
  • 25
  • 115
  • 173
  • 8
    This might have unintended consequences. For instance, if in your base class you have another method that calls DoSomething, and you call SomeClass.ThatOtherMethod(), that method calls the base's DoSomething, not the SomeClass's version. https://dotnetfiddle.net/scRWKD – user420667 Nov 07 '14 at 21:16
  • 2
    throw NotImplementedException in the base class to make sure nothing calls this – Rob Sedgwick Jun 08 '17 at 12:03
16

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 :)

kbolino
  • 1,441
  • 2
  • 18
  • 24
Alan
  • 6,501
  • 1
  • 28
  • 24
  • 11
    The fact that c# does not support static virtual methods drives me crazy... especially considered that c# has been designed by the same guy who designed delphi 10 years before (Anders Hejlsberg) – Carlo Sirna Oct 28 '16 at 08:54
9

I come from Delphi and this is a feature among many that I sorely miss in c#. Delphi would allow you to create typed type references and you could pass the type of a derived class wherever the type of a parent class was needed. This treatment of types as objects had powerful utility. In particular allowing run time determination of meta data. I am horribly mixing syntax here but in c# it would look something like:

    class Root {
       public static virtual string TestMethod() {return "Root"; }
    }
    TRootClass = class of TRoot; // Here is the typed type declaration

    class Derived : Root {
       public static overide string TestMethod(){ return "derived"; }
    }

   class Test {
        public static string Run(){
           TRootClass rc;
           rc = Root;
           Test(rc);
           rc = Derived();
           Test(rc);
        }
        public static Test(TRootClass AClass){
           string str = AClass.TestMethod();
           Console.WriteLine(str);
        }
    } 

would produce: Root derived

Ken Revak
  • 91
  • 1
  • 1
7

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.

Peter Parker
  • 29,093
  • 5
  • 52
  • 80
  • 4
    A virtual method can be overwritten on the type of the instance. All subclasses can share the same implementation of the overloaded function. That's why you want to make them BOTH static and virtual. That's late static binding, available in PHP5. – Alex Weinstein Oct 30 '08 at 19:46
  • 13
    No, there is no contradiction, it is entirely a matter of compiler support, and many languages like Python do it. They do it by having first-class classes. That is, classes that are themselves objects that can be assigned to variables and have their methods called. (Search for "classmethod" to learn more.) – Daniel Newby Mar 10 '10 at 23:55
  • 2
    Object Pascal (Delphi and the open source Lazarus equivalent) have always supported them – Carlo Sirna Oct 28 '16 at 08:55
7

You are not crazy. What you are referring to is called Late Static Binding; it's been recently added to PHP. There's a great thread that describes it - here: When would you need to use late static binding?

Community
  • 1
  • 1
Alex Weinstein
  • 9,823
  • 9
  • 42
  • 59
2

There is a way to force an inheritance of "abstract static" methods from an abstract generic class. See as follow :

public abstract class Mother<T> where T : Mother<T>, new()
{
    public abstract void DoSomething();

    public static void Do()
    {
        (new T()).DoSomething();
    }

}

public class ChildA : Mother<ChildA>
{
    public override void DoSomething() { /* Your Code */ }
}

public class ChildB : Mother<ChildB>
{
    public override void DoSomething() { /* Your Code */ }
}

Example (using the previous Mother):

public class ChildA : Mother<ChildA>
{
    public override void DoSomething() { Console.WriteLine("42"); }
}

public class ChildB : Mother<ChildB>
{
    public override void DoSomething() { Console.WriteLine("12"); }
}

public class Program
{
    static void Main()
    {
        ChildA.Do();  //42
        ChildB.Do();  //12
        Console.ReadKey();
    }
}

It's not that great since you can inherit from only one abstract class and it will ask you to be lenient with your new() implementation.

More, I think it will be costly memory-wise depending on the size of your inherited classes. In case you have memory issue, you would have to set every properties/variables after your new in a public method which is an awful way to have default values.

Lostblue
  • 129
  • 3
1

It is actually possible to combine virtual and static for a method or a member by using the keyword new instead of virtual.

Here is an example:

class Car
{
    public static int TyreCount = 4;
    public virtual int GetTyreCount() { return TyreCount; }
}
class Tricar : Car
{
    public static new int TyreCount = 3;
    public override int GetTyreCount() { return TyreCount; }
}

...

Car[] cc = new Car[] { new Tricar(), new Car() };
int t0 = cc[0].GetTyreCount(); // t0 == 3
int t1 = cc[1].GetTyreCount(); // t1 == 4

Obviously the TyreCount value could have been set in the overridden GetTyreCount method, but this avoids duplicating the value. It is possible to get the value both from the class and the class instance.

Now can someone find a really intelligent usage of that feature?

Mart
  • 5,608
  • 3
  • 32
  • 45
  • 3
    @Mart: I almost downvoted you until I realized you've just got a bad example. Instead of the static fields, change the example to use static methods, where the derived class calls the base class method, then does something more. – John Saunders Apr 08 '10 at 02:51
1

An override method provides a new implementation of a member that is inherited from a base class. The method that is overridden by an override declaration is known as the overridden base method. The overridden base method must have the same signature as the override method. You cannot override a non-virtual or static method. The overridden base method must be virtual, abstract, or override.

An override declaration cannot change the accessibility of the virtual method. Both the override method and the virtual method must have the same access level modifier.

You cannot use the new, static, or virtual modifiers to modify an override method.

An overriding property declaration must specify exactly the same access modifier, type, and name as the inherited property, and the overridden property must be virtual, abstract, or override.

Jigna
  • 75
  • 1
  • 11
1

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.

rslite
  • 81,705
  • 4
  • 44
  • 47
1

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...

charles bretana
  • 246
  • 1
  • 3
1

Mart got it right with the 'new' keyword. I actually got here because I needed this type of functionality and Mart's solution works fine. In fact I took it one better and made my base class method abstract to force the programmer to supply this field.

My scenario was as follows:

I have a base class HouseDeed. Each House type is derived from HouseDeed must have a price.

Here is the partial base HouseDeed class:

public abstract class HouseDeed : Item
{
    public static int m_price = 0;
    public abstract int Price { get; }
    /* more impl here */
}

Now lets look at two derived house types:

public class FieldStoneHouseDeed : HouseDeed
{
    public static new int m_price = 43800;
    public override int Price { get { return m_price; } }
    /* more impl here */
}

and...

public class SmallTowerDeed : HouseDeed
{
    public static new int m_price = 88500;
    public override int Price { get { return m_price; } }
    /* more impl here */
}

As you can see I can access the price of the house via type SmallTowerDeed.m_price, and the instance new SmallTowerDeed().Price And being abstract, this mechanism nags the programmer into supplying a price for each new derived house type.

Someone pointed how 'static virtual' and 'virtual' are conceptually at odds with one another. I disagree. In this example, the static methods do not need access to the instance data, and so the requirements that (1) the price be available via the TYPE alone, and that (2) a price be supplied are met.

Level 42
  • 400
  • 2
  • 7
  • 1
    This answer only seems tangentially related to the question. Also it is a somewhat poor example. m_price is an implementation detail and adds nothing by being part of HouseDeed. It would be much better to remove it from HouseDeed and make m_price private in both sub classes. Same result without the abuse of the 'new' keyword. – Rob McCready May 30 '11 at 23:38
  • 1
    Metadata is not tangent. It is a related need. All overrides are implementation details. You could similarly argue that "for" adds nothing to the language because it has "if" and "goto". – Suncat2000 Jul 21 '21 at 19:14
0

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.

Krauss
  • 998
  • 10
  • 17
0

You will be able to soon, in C# 11!

From Tutorial: Explore C# 11 feature - static virtual members in interfaces:

C# 11 and .NET 7 include static virtual members in interfaces. This feature enables you to define interfaces that include overloaded operators or other static members.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
baddie
  • 437
  • 5
  • 13
  • 1
    A link to a solution is welcome, but please ensure your answer is useful without it: [add context around the link](//meta.stackexchange.com/a/8259) so your fellow users will have some idea what it is and why it is there, then quote the most relevant part of the page you are linking to in case the target page is unavailable. [Answers that are little more than a link may be deleted.](/help/deleted-answers) – Ethan Sep 10 '22 at 00:57
0

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);
        }
    }
}
wickedone
  • 542
  • 1
  • 6
  • 18