Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaration of a abstract generic type variable

my problem is the following. I have made a code design for a home project which is apparently not working. Maybe you can help me to figure out where the "code smell" comes from.

Ok let's start: I have defined some classes to wrap around different kind of archive types:

public abstract class Archive { }
public class ZipArchive : Archive { }
public class TarArchive : Archive { }

To handle with those archives, I defined Manager classes. An abstract one that defines the needed behaviour,

public abstract class ArchiveManager<T> where T : Archive
{ 
    public abstract void OpenArchive(T archive);
}

And the concrete ones, that actually implement the specific behaiour:

public class ZipArchiveManager : ArchiveManager<ZipArchive>
{
    public override void OpenArchive(ZipArchive archive) {  /* .. */ }
}

public class TarArchiveManager : ArchiveManager<TarArchive>
{
    public override void OpenArchive(TarArchive archive) {  /* .. */ }
}

What happens now is that during compile time, I dont know which kind of archives I will process, so I tried the following:

class Program
{
    static void Main(string[] args)
    {
        ArchiveManager<Archive> archiveManager = null;

        if (/*some condition*/) {            
            archiveManager = new ZipArchiveManager();
        }
        else {
            archiveManager = new TarArchiveManager();
        }
    }
}

which ended up in the following error:

Cannot implicitly convert type 'ZipArchiveManager' to 'ArchiveManager'

As far as I understand, the generic argument cannot be implicitely converted. Is there any way to come around this? Does this code / design "smell"?

Thank you very much in advance.

like image 837
Flagg1980 Avatar asked Nov 04 '22 01:11

Flagg1980


1 Answers

You can use a contravariant interface instead of an abstract class that doesn't implement any functionality. In this case, you can only use the type parameter as a return value of a method, not as an argument:

public interface IArchiveManager<out T>
    where T : Archive
{
    T OpenArchive(Stream stream);
}

Then, simply implement the interface in your manager classes:

public class ZipArchiveManager : IArchiveManager<ZipArchive>
{
    public ZipArchive OpenArchive(Stream stream)
    {
        // ...
    }
}

public class TarArchiveManager : IArchiveManager<TarArchive>
{
    public TarArchive OpenArchive(Stream stream)
    {
        // ...
    }
}
like image 119
Michael Avatar answered Nov 09 '22 05:11

Michael