🎓 A1Learner
🔍
0 of 28 lessons completed

C# Static Abstract Members

A static abstract member declares a static operation in an interface without giving its body. Each implementing type supplies its own version of that operation.

An interface with a static abstract method

The interface asks for an Add method and a Zero property. The generic Total method adds through the interface alone.

using System;

class Program
{
    static T Total<T>(T a, T b, T c) where T : IAdder<T>
    {
        T sum = T.Zero;
        sum = T.Add(sum, a);
        sum = T.Add(sum, b);
        sum = T.Add(sum, c);
        return sum;
    }

    static void Main(string[] args)
    {
        Score total = Total(new Score(3), new Score(4), new Score(5));
        Console.WriteLine(total.Value);
    }
}

interface IAdder<T>
{
    static abstract T Add(T a, T b);
    static abstract T Zero { get; }
}

struct Score : IAdder<Score>
{
    public int Value;

    public Score(int value)
    {
        Value = value;
    }

    public static Score Add(Score a, Score b)
    {
        return new Score(a.Value + b.Value);
    }

    public static Score Zero
    {
        get { return new Score(0); }
    }
}

A second type with the same contract

The Meters struct implements the same interface over a double value. The generic Sum method adds 1.5 and 2.5 through the contract.

using System;

class Program
{
    static T Sum<T>(T a, T b) where T : IAdder<T>
    {
        return T.Add(a, b);
    }

    static void Main(string[] args)
    {
        Meters sum = Sum(new Meters(1.5), new Meters(2.5));
        Console.WriteLine(sum.Value);
    }
}

interface IAdder<T>
{
    static abstract T Add(T a, T b);
    static abstract T Zero { get; }
}

struct Meters : IAdder<Meters>
{
    public double Value;

    public Meters(double value)
    {
        Value = value;
    }

    public static Meters Add(Meters a, Meters b)
    {
        return new Meters(a.Value + b.Value);
    }

    public static Meters Zero
    {
        get { return new Meters(0); }
    }
}

A static abstract factory method

The IFactory contract asks each type to create an instance of itself. The generic Make method calls Create through the type parameter.

using System;

class Program
{
    static T Make<T>() where T : IFactory<T>
    {
        return T.Create();
    }

    static void Main(string[] args)
    {
        Gadget gadget = Make<Gadget>();
        Console.WriteLine(gadget.Code);
    }
}

interface IFactory<T>
{
    static abstract T Create();
}

class Gadget : IFactory<Gadget>
{
    public int Code;

    public Gadget(int code)
    {
        Code = code;
    }

    public static Gadget Create()
    {
        return new Gadget(7);
    }
}

Example

using System;

class Program
{
    static T Twice<T>(T value) where T : IAdder<T>
    {
        return T.Add(value, value);
    }

    static void Main(string[] args)
    {
        Score doubled = Twice(new Score(21));
        Console.WriteLine(doubled.Value);
    }
}

interface IAdder<T>
{
    static abstract T Add(T a, T b);
    static abstract T Zero { get; }
}

struct Score : IAdder<Score>
{
    public int Value;

    public Score(int value)
    {
        Value = value;
    }

    public static Score Add(Score a, Score b)
    {
        return new Score(a.Value + b.Value);
    }

    public static Score Zero
    {
        get { return new Score(0); }
    }
}

Key points

  • A static abstract member can be a method or a property.
  • The implementing type writes the member as a public static member.
  • A generic method can call the member through its type parameter.
  • The constraint where T : IAdder<T> ties the type parameter to the contract.
  • A static abstract property can stand for a starting value, such as a zero.
  • The framework groups contracts of this kind in interfaces such as INumber<T>.

In this example, the Twice method takes any type that implements IAdder<T>. It passes the value to Add twice, once as each argument. A Score of 21 doubles to 42.

Note

The call site names the type parameter, never an instance, because the member is static.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Why can the generic Total method call T.Add?