简体   繁体   中英

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.

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);
    }
}

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);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM