Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Self-referenced generic parameter

For example I have the following classes:

1.

class MyClass1
{
    public MyClass1 Method()
    {
        ...
        return new MyClass1();
    }
}

class MyClass2
{
    public MyClass2 Method()
    {
        ...
        return new MyClass2();
    }
}

The methods have the same bodies that's why I want to extract the code and re-use.

2.

abstract class MyClass
{

    protected void Method()
    {
        ...
    }
}

class MyClass1 : MyClass
{

    public MyClass1 Method()
    {
        base.Method();
        return new MyClass1();
    }
}

class MyClass2 : MyClass
{
    public MyClass2 Method()
    {
        base.Method();
        return new MyClass2();
    }
}

However since there are a lot of such methods it will be better to move the methods into the base class MyClass at all:

3.

abstract class MyClass<T>: where T : MyClass<T>
{
    protected abstract T Create();

    public T Method()
    {
        ...
        return Create();
    }
}

class MyClass1 : MyClass<MyClass1>
{
    protected override MyClass1 Create() => new MyClass1();
}

class MyClass2 : MyClass<MyClass2>
{
    protected override MyClass2 Create() => new MyClass2();
}

It works fine but the contract looks too weird. Of course, I can extract something like IMyInterface and return it instead of the class. However I have to preserve the original contract because it contains specific methods as well.

Upd: So, the weird thing is in bold - class MyClass1: MyClass<MyClass1>

like image 374
Serg046 Avatar asked Feb 17 '19 14:02

Serg046


1 Answers

This is the usual so-called self-type problem (you have Method() should return the same type as the object on which it was called). Your solution #3 looks a lot like F-bounded quantification. However, this is C#, not Java, so we can do a bit better using an extension class.

You can make sure those methods only get called on subclasses of MyClass by adding a where T : MyClass bound on T.

// Put all your shared methods in generic classes in here.
public static class MyMethods
{
    public static T Method<T>(this T x) where T : MyClass
    {
        ...
    }
}

Your classes don't change much, except they won't need to mention Method (or the other shared methods) at all.

public abstract class MyClass
{
    ...
}

public class MyClass1 : MyClass
{
    ...
}

public class MyClass2 : MyClass
{
    ...
}
like image 108
Alec Avatar answered Oct 05 '22 00:10

Alec