Как создать новое static поле в наследнике или КАК СЭКОНОМИТЬ пару байт?

У меня имеется класс Packet, от которого наследуются другие Packet'ы.

public abstract class Packet
{
    public static byte Id;

    public virtual void Write(ref RawPacket packet)
    {
        packet.WriteByte(Id);
    }
}

Как сделать так чтобы для каждого наследника этого класса был свой static Id, чтобы сэкономить пару байт? Пытался выносить Id в Static Generic классы, но тогда метод Write не видит Id.

Например у меня имеется PacketA, PacketB, которые наследуются от класса Packet. И ко мне по сети приходит массив байт, первый элемент которого указывает на тип пакета, то есть Id. И с помощью Id, используя Dictionary<byte, Type> я могу обратится к экземпляру класса PacketA или B и узнать каким способом считывать этот массив. А экземпляры пакетов PacketA, PacketB очень часто создаются, потому Id в экземплярах одних и тех же PacketA или PacketB будут расходовать лишний байт памяти на каждое Id.


Ответы (3 шт):

Автор решения: Qwer

Можно вместо статики прописать в базовом классе абстрактное свойство:

public abstract class Packet
{
    public abstract byte Id
    {
        get;
    }

    public virtual void Write (ref RawPacket packet)
    {
        packet.WriteByte(Id);
    }
}

И имплементировать это свойство в производных классах:

public class PacketA : Packet
{
    public override byte Id => 1;
}

public class PacketB : Packet
{
    public override byte Id => 2;
}
→ Ссылка
Автор решения: rotabor

Вам нужно объявлять Id в каждом наследнике:

public class PacketA : Packet
{
    public static byte Id;

    public override void Write(ref RawPacket packet)
    {
        packet.WriteByte(Id);
    }
}

Скорее всего, Id как-то связан с классом, так что он может быть константой.

Проверил CRTP by VladD:

using System;
static class Programm {
    public abstract class Packet<TSelf> where TSelf : Packet<TSelf> {
        public static byte Id;
        public void Write() { Console.WriteLine(Id); }
    }
    public class Ipv4Packet : Packet<Ipv4Packet> {
        static Ipv4Packet() { Id = 1; }
    }
    public class Ipv6Packet : Packet<Ipv6Packet> {
        static Ipv6Packet() { Id = 2; }
    }
    static void Main() {
        var a1 = new Ipv4Packet(); a1.Write();
        var a2 = new Ipv6Packet(); a2.Write();
        a1.Write();
        a2.Write();
    }
}

-->

1
2
1
2

Можно взять на вооружение!

→ Ссылка
Автор решения: VladD

Например, можно применить CRTP:

public abstract class Packet<TSelf> where TSelf : Packet<TSelf>
{
    public static byte Id;

    public virtual void Write(ref RawPacket packet)
    {
        packet.WriteByte(Id);
    }
}

Использовать нужно так:

public class Ipv4Packet : Packet<Ipv4Packet>
{
    // ...
}

Заметьте, что с CRTP различные пакеты не имеют общего предка, так что если он реально нужен, вам понадобится унаследовать Packet<TSelf> от нешаблонного Packet:

public abstract class Packet
{
    // общая функциональность тут, возможно как abstract
}

public abstract class Packet<TSelf> : Packet where TSelf : Packet<TSelf>
{
    // ...
→ Ссылка