Feofilakt
Feofilakt

Reputation: 1388

Polymorphism in C# generic types

Why can't generics be used like this? Declare common open type or interface and separate logic for each concrete type directly:

interface IOpen<T>
{
    T A { get; }
}

class Concrete<int> : IOpen<int>
{
    public int A => 42;
    public string B => "42";
}

interface IWorker<T>
{
    void Do(IOpen<T> item);
}

class WorkerInt : IWorker<int>
{
    public void Do(Concrete<int> item)
    {
        Console.WriteLine(item.A);
        Console.WriteLine(item.B);
    }
}

How to avoid that restriction in the code above? If I create class ConcreteInt : IOpen<int> then WorkerInt would not implement IWorker<T>. Thanks.

Upvotes: 0

Views: 986

Answers (2)

Enigmativity
Enigmativity

Reputation: 117009

You can't define class Concrete<int> with <int> - it's like you're trying to override the normal definition of int with a new generic type called int. But then in the class you're trying to actually return an int.

So it should look like:

class Concrete : IOpen<int>
{
    public int A => 42;
    public string B => "42";
}

But now the class WorkerInt would have to look like this:

class WorkerInt : IWorker<int>
{
    public void Do(Concrete item)
    {
        Console.WriteLine(item.A);
        Console.WriteLine(item.B);
    }
}

But IWorker<int> must implement void Do(IOpen<T> item) and even though Concrete implements IOpen<T> you can't use void Do(Concrete item) because it is more restrictive than void Do(IOpen<T> item).

So you must define it this way:

class WorkerInt : IWorker<int>
{
    public void Do(IOpen<int> item)
    {
        Console.WriteLine(item.A);
        //Console.WriteLine(item.B);
    }
}

But that makes item.B no longer work as IOpen<int> doesn't have a B property.

The only way to make this work is to change IWorker to be this:

interface IWorker<T, R> where T : IOpen<R>
{
    void Do(T item);
}

Then WorkerInt can be this:

class WorkerInt : IWorker<Concrete, int>
{
    public void Do(Concrete item)
    {
        Console.WriteLine(item.A);
        Console.WriteLine(item.B);
    }
}

Upvotes: 2

Soviut
Soviut

Reputation: 91515

Generics aren't meant as a placeholder for type inference. Generics are meant to allow containers like List, Dictionary, Tree, etc. to contain any type without needing to cast to and from Object. They make containers more statically robust.

If you want to be able to pass a limited set of types to some kind of processor, then use overloaded methods.

public void Do(int item) { ... }

public void Do(string item) { ... }

This way, the method signature is what determines which method to use.

Additionally, if you're trying to make different worker objects, you could have a similar set of static overloaded methods that instantiate the workers and call the Do() method.

class WorkerManager {
    public static void DoWork(int item) {
        var worker = new WorkerInt();
        worker.Do(item);
    }

    public static void DoWork(string item) {
        var worker = new WorkerString();
        worker.Do(item);
    }
}

Upvotes: 0

Related Questions