Tommaso Belluzzo
Tommaso Belluzzo

Reputation: 23685

Abstract Class with Generic Subtype

I implemented an abstract class like the following:

public abstract class Pack
{
    protected List<PackEntry> m_Entries;

    public Int32 EntriesCount
    {
        get { return m_Entries.Count; }
    }

    public Pack()
    {
        m_Entries = new List<PackEntry>();
    }

    #region Methods: Abstract
    ...
    #endregion
}

public sealed class PackTypeA : Pack { ... }
public sealed class PackTypeB : Pack { ... }

And a PackEntry subclass also:

public abstract class PackEntry
{
    protected Byte[] m_Data;

    protected PackEntry() { }

    public Byte[] GetData()
    {
        return m_Data;
    }

    public void SetData(Byte[] data)
    {
        m_Data = data;
    }

    #region Methods: Abstract
    ...
    #endregion
}

public sealed class PackEntryTypeA : PackEntry { ... }
public sealed class PackEntryTypeB : PackEntry { ... }

This means that the derived class PackTypeA will use PackEntryTypeA entries and PackTypeB will use PackEntryTypeB and so on. Now... I'm using my Pack abstract class as in the following code snippet:

Pack pack = useTypeA ? new PackTypeA() : new PackTypeB();
pack.AbstractMethod1();

if (condition)
    pack.AbstractMethod2();

...

Everything works like a charm but inside AbstractMethod1() ... AbstractMethodN() overrides I'm working a lot with the entries list. Which needs a lot of casting like:

for (Int32 i = 0; i < m_Entries.Count; ++i)
{
    PackEntryTypeA entry = (PackEntryTypeA)m_Entries[i];
    ...

It's getting tedious. So I tried the following approach implementing a generic type to my Pack class:

public abstract class Pack<T> where T : PackEntry
{
    protected List<T> m_Entries;

    public Int32 EntriesCount
    {
        get { return m_Entries.Count; }
    }

    public Pack()
    {
        m_Entries = new List<T>();
    }

    #region Methods: Abstract
    ...
    #endregion
}

public sealed class PackTypeA : Pack<PackEntryTypeA> { ... }
public sealed class PackTypeB : Pack<PackEntryTypeB> { ... }

Which is much, much, much better! But the problem is that I can't use the following code snippet anymore:

Pack pack = useTypeA ? new PackTypeA() : new PackTypeB();

Because it requires the generic type to be specified (which I really can't):

Pack<PackTypeN>

The last solution I figured out is to move the list to the derived classes but it's not very elegant. Can someone kindly point me to a solution please?

Upvotes: 0

Views: 174

Answers (1)

aush
aush

Reputation: 2108

Pack<PackEntryTypeA> and Pack<PackEntryTypeB> are different types, so, to be able to store them in a same variable they have to have same base class or interface.

public static void Main()
{
    var pack = 1 == 1 ? (IPack)new PackTypeA() : (IPack)new PackTypeB();
    pack.AbstractMethod1();
    pack = 1 == 2 ? (IPack)new PackTypeA() : (IPack)new PackTypeB();
    pack.AbstractMethod1();
}

public interface IPack { void AbstractMethod1(); }

public abstract class PackEntry { }

public sealed class PackEntryTypeA : PackEntry {}
public sealed class PackEntryTypeB : PackEntry {}

public abstract class Pack<T> : IPack where T : PackEntry
{
    protected List<T> m_Entries = new List<T>();
    public abstract void AbstractMethod1();
}

public sealed class PackTypeA : Pack<PackEntryTypeA> 
{
    public override void AbstractMethod1() 
    { 
        Console.WriteLine(m_Entries.GetType().ToString());
    }
}

public sealed class PackTypeB : Pack<PackEntryTypeB> 
{
    public override void AbstractMethod1() 
    { 
        Console.WriteLine(m_Entries.GetType().ToString());
    }
}

Output:

System.Collections.Generic.List`1[PackEntryTypeA]
System.Collections.Generic.List`1[PackEntryTypeB]

Upvotes: 2

Related Questions