Why can't I have abstract static methods in C#?
Asked Answered
A

10

215

I've been working with providers a fair bit lately, and I came across an interesting situation where I wanted to have an abstract class that had an abstract static method. I read a few posts on the topic, and it sort of made sense, but is there a nice clear explanation?

Accordingly answered 6/8, 2008 at 11:4 Comment(9)
Please leave these open to allow to future improvements.Unsuspecting
Possible duplicate of How to implement virtual static properties?Strive
As a side note, TextWriter.Null and StreamWriter.Null shown a similar attempt around this topic.Admission
As pointed out in a newish answer this is now available for interfaces in the latest C#: https://mcmap.net/q/82836/-why-can-39-t-i-have-abstract-static-methods-in-cMimicry
I don't understand a use case for abstract static methods? How are they going to be defined later since static methods have no inheritance? Is this another case for needing static method definitions in interfaces?Intention
@Intention you may find this interesting, it has plenty of examples & detailed discussion of the feature: github.com/dotnet/csharplang/issues/4436Mimicry
@Mimicry - I would love to see static methods/operators in interfaces, especially for numeric types since operators have to be static. But in this case here I think we're talking about something different.Intention
@Intention the link above describes only abstract static methods/properties, not virtual (with a default implementation), maybe that's what you mean. There is a section there which talks about potentially doing that later. However they cite the operator cases as one of the specific motivations of the static abstract feature, maybe that's similar to what you are thinking. They give an IAddable example.Mimicry
@Mimicry - we'll see if it ever gets implemented. There similar discussion going back to .NET 2.0 about this.Intention
P
164

Static methods are not instantiated as such, they're just available without an object reference.

A call to a static method is done through the class name, not through an object reference, and the Intermediate Language (IL) code to call it will call the abstract method through the name of the class that defined it, not necessarily the name of the class you used.

Let me show an example.

With the following code:

public class A
{
    public static void Test()
    {
    }
}

public class B : A
{
}

If you call B.Test, like this:

class Program
{
    static void Main(string[] args)
    {
        B.Test();
    }
}

Then the actual code inside the Main method is as follows:

.entrypoint
.maxstack 8
L0000: nop 
L0001: call void ConsoleApplication1.A::Test()
L0006: nop 
L0007: ret 

As you can see, the call is made to A.Test, because it was the A class that defined it, and not to B.Test, even though you can write the code that way.

If you had class types, like in the Delphi Programming Language, where you can make a variable referring to a type and not an object, you would have more use for virtual and thus abstract static methods (and also constructors), but they aren't available and thus static calls are non-virtual in .NET.

I realize that the IL designers could allow the code to be compiled to call B.Test, and resolve the call at runtime, but it still wouldn't be virtual, as you would still have to write some kind of class name there.

Virtual methods, and thus abstract ones, are only useful when you're using a variable which, at runtime, can contain many different types of objects, and you thus want to call the right method for the current object you have in the variable. With static methods you need to go through a class name anyway, so the exact method to call is known at compile time because it can't and won't change.

Thus, virtual/abstract static methods are not available in .NET.

Portal answered 6/8, 2008 at 11:30 Comment(4)
Combined with the way operator-overloading is done in C#, this unfortunately eliminates the possibility of requiring subclasses to provide an implementation for a given operator overload.Tabbatha
I don't find this answer terribly useful as the definition of Test() is in A rather than being abstract and potentially defined in B.\Burck
Generic type parameters effectively behave as non-persistable "type" variables, and virtual static methods could be useful in such context. For example, if one had a Car type with a virtual static CreateFromDescription factory method, then code which accepted a Car-constrained generic type T could call T.CreateFromDescription to produce a car of type T. Such a construct could be supported pretty well within the CLR if each type which defined such a method held a static singleton instance of a nested class generic which held the virtual "static" methods.Myrta
B.Test() is wrong and the compiler should flag it. Use A.Test() since that is the class where the static method is defined under.Intention
D
46

Static methods cannot be inherited or overridden, and that is why they can't be abstract. Since static methods are defined on the type, not the instance, of a class, they must be called explicitly on that type. So when you want to call a method on a child class, you need to use its name to call it. This makes inheritance irrelevant.

Assume you could, for a moment, inherit static methods. Imagine this scenario:

public static class Base
{
    public static virtual int GetNumber() { return 5; }
}

public static class Child1 : Base
{
    public static override int GetNumber() { return 1; }
}

public static class Child2 : Base
{
    public static override int GetNumber() { return 2; }
}

If you call Base.GetNumber(), which method would be called? Which value returned? It's pretty easy to see that without creating instances of objects, inheritance is rather hard. Abstract methods without inheritance are just methods that don't have a body, so can't be called.

Dmso answered 6/8, 2008 at 11:21 Comment(6)
Given your scenario i would say Base.GetNumber() would return 5; Child1.GetNumber() returns 1; Child2.GetNumber() returns 2; Can you prove me wrong, to help me understand your reasoning? Thank youSomewhere
The face that you think Base.GetNumber() returns 5, means that you already understand what is going on. By returning the base value, there is no inheritance going on.Dmso
Why in the world would Base.GetNumber() return anything else but 5? It's a method in the base class - there's only 1 option there.Cindelyn
@ArtemRussakovskii: Suppose one had int DoSomething<T>() where T:Base {return T.GetNumber();}. It would seem useful if DoSomething<Base>() could return five, while DoSomething<Child2>() would return two. Such ability would be not only useful for toy examples, but also for something like class Car {public static virtual Car Build(PurchaseOrder PO);}, where every class deriving from Car would have to define a method which could build an instance given a purchase order.Myrta
There is exactly same "problem" with non-static inheritance.Megaton
This is not a valid answer at all. As some others said, Base.GetNumber() must return 5 always. Child 1 & 2 must return 1 and 2 respectively. Nothing else makes sense.Bree
G
22

Another respondent (McDowell) said that polymorphism only works for object instances. That should be qualified; there are languages that do treat classes as instances of a "Class" or "Metaclass" type. These languages do support polymorphism for both instance and class (static) methods.

C#, like Java and C++ before it, is not such a language; the static keyword is used explicitly to denote that the method is statically-bound rather than dynamic/virtual.

Gerber answered 9/8, 2008 at 7:1 Comment(0)
R
19

With .NET 6 / C# 11/next/preview you are able to do exactly that with "Static abstract members in interfaces".

(At the time of writing the code compiles successfully but some IDEs have problems highlighting the code)

SharpLab Demo

using System;

namespace StaticAbstractTesting
{
    public interface ISomeAbstractInterface
    {
        public abstract static string CallMe();
    }

    public class MyClassA : ISomeAbstractInterface
    {
        static string ISomeAbstractInterface.CallMe()
        {
            return "You called ClassA";
        }
    }

    public class MyClassB : ISomeAbstractInterface
    {
        static string ISomeAbstractInterface.CallMe()
        {
            return "You called ClassB";
        }
    }

    public class Program
    {

        public static void Main(string[] args)
        {
            UseStaticClassMethod<MyClassA>();
            UseStaticClassMethod<MyClassB>();
        }

        public static void UseStaticClassMethod<T>() where T : ISomeAbstractInterface
        {
            Console.WriteLine($"{typeof(T).Name}.CallMe() result: {T.CallMe()}");
        }
    }
}

Since this is a major change in the runtime, the resulting IL code also looks really clean, which means that this is not just syntactic sugar.

public static void UseStaticClassMethodSimple<T>() where T : ISomeAbstractInterface {
IL_0000: constrained. !!T
IL_0006: call string StaticAbstractTesting.ISomeAbstractInterface::CallMe()
IL_000b: call void [System.Console]System.Console::WriteLine(string)
IL_0010: ret
}

Resources:

Rubbico answered 21/10, 2021 at 18:10 Comment(1)
And currently planned for proper release in C#11.Ashlar
K
15

This question is 12 years old but it still needs to be given a better answer. As few noted in the comments and contrarily to what all answers pretend it would certainly make sense to have static abstract methods in C#. As philosopher Daniel Dennett puts it, a failure of imagination is not an insight into necessity. It is a common mistake not to realize that C# is more than an OOP language. A pure OOP perspective on a given concept leads to a restricted - and in the current case misguided - examination. Polymorphism is not only about subtyping polymorphism: it also includes parametric polymorphism (aka generic programming) and C# has been supporting this for a long time now. Within this additional paradigm, abstract classes (and most types) are not only used to provide a type to instances. They can also be used as bounds for generic parameters; something that has been understood by users of certain languages (like for example Haskell, but also more recently Scala, Rust or Swift) for years.

In this context you may want to do something like this:

void Catch<TAnimal>() where TAnimal : Animal
{
    string scientificName = TAnimal.ScientificName; // abstract static property
    Console.WriteLine($"Let's catch some {scientificName}");
    …
}

And here the capacity to express static members that can be specialized by subclasses totally makes sense!

Unfortunately C# does not allow abstract static members but I'd like to propose a pattern that can emulate them reasonably well. This pattern is not perfect (it imposes some restrictions on inheritance) but as far as I can tell it is typesafe.

The main idea is to associate an abstract generic companion class (here SpeciesFor<TAnimal>) to the one that should contain static abstract members (here Animal):

public abstract class SpeciesFor<TAnimal> where TAnimal : Animal
{
    public static SpeciesFor<TAnimal> Instance { get { … } }

    // abstract "static" members

    public abstract string ScientificName { get; }
    
    …
}

public abstract class Animal { … }

Now we would like to make this work:

void Catch<TAnimal>() where TAnimal : Animal
{
    string scientificName = SpeciesFor<TAnimal>.Instance.ScientificName;
    Console.WriteLine($"Let's catch some {scientificName}");
    …
}

Of course we have two problems to solve:

  1. How do we make sure an implementer of a subclass of Animal provides a specific instance of SpeciesFor<TAnimal> to this subclass?
  2. How does the property SpeciesFor<TAnimal>.Instance retrieve this information?

Here is how we can solve 1:

public abstract class Animal<TSelf> where TSelf : Animal<TSelf>
{
    private Animal(…) {}
    
    public abstract class OfSpecies<TSpecies> : Animal<TSelf>
        where TSpecies : SpeciesFor<TSelf>, new()
    {
        protected OfSpecies(…) : base(…) { }
    }
    
    …
}

By making the constructor of Animal<TSelf> private we make sure that all its subclasses are also subclasses of inner class Animal<TSelf>.OfSpecies<TSpecies>. So these subclasses must specify a TSpecies type that has a new() bound.

For 2 we can provide the following implementation:

public abstract class SpeciesFor<TAnimal> where TAnimal : Animal<TAnimal>
{
    private static SpeciesFor<TAnimal> _instance;

    public static SpeciesFor<TAnimal> Instance => _instance ??= MakeInstance();

    private static SpeciesFor<TAnimal> MakeInstance()
    {
        Type t = typeof(TAnimal);
        while (true)
        {
            if (t.IsConstructedGenericType
                    && t.GetGenericTypeDefinition() == typeof(Animal<>.OfSpecies<>))
                return (SpeciesFor<TAnimal>)Activator.CreateInstance(t.GenericTypeArguments[1]);
            t = t.BaseType;
            if (t == null)
                throw new InvalidProgramException();
        }
    }

    // abstract "static" members

    public abstract string ScientificName { get; }
    
    …
}

How do we know that the reflection code inside MakeInstance() never throws? As we've already said, almost all classes within the hierarchy of Animal<TSelf> are also subclasses of Animal<TSelf>.OfSpecies<TSpecies>. So we know that for these classes a specific TSpecies must be provided. This type is also necessarily constructible thanks to constraint : new(). But this still leaves out abstract types like Animal<Something> that have no associated species. Now we can convince ourself that the curiously recurring template pattern where TAnimal : Animal<TAnimal> makes it impossible to write SpeciesFor<Animal<Something>>.Instance as type Animal<Something> is never a subtype of Animal<Animal<Something>>.

Et voilà:

public class CatSpecies : SpeciesFor<Cat>
{
    // overriden "static" members

    public override string ScientificName => "Felis catus";
    public override Cat CreateInVivoFromDnaTrappedInAmber() { … }
    public override Cat Clone(Cat a) { … }
    public override Cat Breed(Cat a1, Cat a2) { … }
}

public class Cat : Animal<Cat>.OfSpecies<CatSpecies>
{
    // overriden members

    public override string CuteName { get { … } }
}

public class DogSpecies : SpeciesFor<Dog>
{
    // overriden "static" members

    public override string ScientificName => "Canis lupus familiaris";
    public override Dog CreateInVivoFromDnaTrappedInAmber() { … }
    public override Dog Clone(Dog a) { … }
    public override Dog Breed(Dog a1, Dog a2) { … }
}

public class Dog : Animal<Dog>.OfSpecies<DogSpecies>
{
    // overriden members

    public override string CuteName { get { … } }
}

public class Program
{
    public static void Main()
    {
        ConductCrazyScientificExperimentsWith<Cat>();
        ConductCrazyScientificExperimentsWith<Dog>();
        ConductCrazyScientificExperimentsWith<Tyranosaurus>();
        ConductCrazyScientificExperimentsWith<Wyvern>();
    }
    
    public static void ConductCrazyScientificExperimentsWith<TAnimal>()
        where TAnimal : Animal<TAnimal>
    {
        // Look Ma! No animal instance polymorphism!
        
        TAnimal a2039 = SpeciesFor<TAnimal>.Instance.CreateInVivoFromDnaTrappedInAmber();
        TAnimal a2988 = SpeciesFor<TAnimal>.Instance.CreateInVivoFromDnaTrappedInAmber();
        TAnimal a0400 = SpeciesFor<TAnimal>.Instance.Clone(a2988);
        TAnimal a9477 = SpeciesFor<TAnimal>.Instance.Breed(a0400, a2039);
        TAnimal a9404 = SpeciesFor<TAnimal>.Instance.Breed(a2988, a9477);
        
        Console.WriteLine(
            "The confederation of mad scientists is happy to announce the birth " +
            $"of {a9404.CuteName}, our new {SpeciesFor<TAnimal>.Instance.ScientificName}.");
    }
}

A limitation of this pattern is that it is not possible (as far as I can tell) to extend the class hierarchy in a satisfying manner. For example we cannot introduce an intermediary Mammal class associated to a MammalClass companion. Another is that it does not work for static members in interfaces which would be more flexible than abstract classes.

Krenek answered 5/2, 2021 at 21:35 Comment(0)
P
11

Here is a situation where there is definitely a need for inheritance for static fields and methods:

abstract class Animal
{
  protected static string[] legs;

  static Animal() {
    legs=new string[0];
  }

  public static void printLegs()
  {
    foreach (string leg in legs) {
      print(leg);
    }
  }
}


class Human: Animal
{
  static Human() {
    legs=new string[] {"left leg", "right leg"};
  }
}


class Dog: Animal
{
  static Dog() {
    legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"};
  }
}


public static void main() {
  Dog.printLegs();
  Human.printLegs();
}


//what is the output?
//does each subclass get its own copy of the array "legs"?
Pratte answered 18/2, 2010 at 5:36 Comment(2)
No, there's only one instance of the array 'legs'. Output is nondeterministic as you don't know what order the static constructors will be called (there's actually no guarantee the base class static constructor would be called at all). 'Need' is a fairly absolute term where 'desire' is probably more accurate.Fulmination
legs should be a static abstract property.Ferrosilicon
E
9

To add to the previous explanations, static method calls are bound to a specific method at compile-time, which rather rules out polymorphic behavior.

Expectorant answered 6/8, 2008 at 11:24 Comment(6)
C# is statically typed; calls to polymorphic methods are also bound at compile time as I understand it - that is to say the CLR is not left to resolve which method to call during runtime.Ilonailonka
So how exactly do you think polymorphism works on the CLR? Your explanation just ruled out virtual method dispatch.Expectorant
That's not really as useful a comment as it could be. I invited (with 'as I understand it') useful discourse, think maybe you could provide a little more content - seeing as people come here looking for answers and not insults. Although, it seems I may be guilty of the same thing - I really meant the above comment as a question: Doesn't C# evaluate these things at compile time?Ilonailonka
Apologies, I didn't mean an insult (although I do admit to responding a bit snappily ;-). The point of my question was, if you've got these classes: class Base { public virtual void Method(); } class Derived : Base { public override void Method(); } and write thusly: Base instance = new Derived(); instance.Method(); the compile-time type information on the call site is that we've got an instance of Base, when the actual instance is a Derived. So the compiler can't resolve the exact method to call. Instead it emits a "callvirt" IL instruction that tells the runtime to dispatch..Expectorant
... whereas for static methods, there's no instance to do virtual dispatch on, so it always necessarily emits a "call" instruction. And now that I expanded on my answer, I realize what a crappy answer it was. ;-)Expectorant
Thanks man, thats informative! Guess I have been putting off the dive into IL long enough, wish me luck.Ilonailonka
K
6

We actually override static methods (in delphi), it's a bit ugly, but it works just fine for our needs.

We use it so the classes can have a list of their available objects without the class instance, for example, we have a method that looks like this:

class function AvailableObjects: string; override;
begin
  Result := 'Object1, Object2';
end; 

It's ugly but necessary, this way we can instantiate just what is needed, instead of having all the classes instantianted just to search for the available objects.

This was a simple example, but the application itself is a client-server application which has all the classes available in just one server, and multiple different clients which might not need everything the server has and will never need an object instance.

So this is much easier to maintain than having one different server application for each client.

Hope the example was clear.

Kellikellia answered 18/8, 2008 at 19:1 Comment(0)
T
1

The abstract methods are implicitly virtual. Abstract methods require an instance, but static methods do not have an instance. So, you can have a static method in an abstract class, it just cannot be static abstract (or abstract static).

Tm answered 14/5, 2009 at 11:11 Comment(1)
-1 virtual methods do not need an instance, except by design. And you do not actually address the question, so much as deflect it.Tableau
S
1

It's available in C# 10 as a preview feature for now.

Shrunken answered 22/12, 2021 at 20:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.