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?
This question is related to
c#
.net
language-design
To add to the previous explanations, static method calls are bound to a specific method at compile-time, which rather rules out polymorphic behavior.
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).
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.
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 put it, a failure of imagination is not an insight into necessity. There is a common mistake in not realizing that C# is not only 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 subtying 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 type 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 companion class (here SpeciesFor<TAnimal>
) to the one that should contain 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:
Animal
to associate a specific instance of SpeciesFor<TAnimal>
to this subclass?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 can we be sure 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 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 satifying 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.
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.
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"?
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.
Source: Stackoverflow.com