This project is read-only.

Generic pendant for this (TSelf)

Topics: C# Language Design
Aug 29, 2014 at 12:42 PM
Hi,

one thing i miss every time i develop libraries which use a lot of generics is,
the generic self type referencing. I use it for fluent invocations. Sometimes it is useful to get the return type of the current instance type rather then the base class.

Example: I do following currently to get something similar, but it has its drawbacks.
public abstract class BaseClass<TSelf>
    where TSelf : BaseClass<TSelf>
{
    public virtual TSelf Foo()
    {
        // Do something which is only implemented in base class.
        return (TSelf)this;
    }
}

public class ChildClass : BaseClass<ChildClass>
{
    public override ChildClass Foo()
    {
        return this;
    }
}
So the problem is now if i want to make the class also extensible, but not to implement all variants of all overloads that the call to a Foo method is also invoked in the correct scope.
And the most important thing, that i'm able to instantiate such a class ;)
public class ChildClass<TSelf> : BaseClass<TSelf>
    where TSelf : ChildClass<TSelf>
{
    public override TSelf Foo()
    {
        base.Foo();
        
        // Do something.
        
        return this;
    }
}

// Not working
new ChildClass<???>
So is it possible that the compiler can help out here?!?
If I do not have a lapse of thought, this could be solved with an additional language keyword or like extension methods with the "this" keyword and additional syntax and semantic analyzing and inference.
Sep 20, 2014 at 7:43 PM
Has nobody those issues, or is nobody using recursive generics?
Sep 21, 2014 at 6:15 PM
I didn't comment because I use this inheritance pattern rather often, and I don't understand the problem you are trying to solve.

In my work, the fact there is a BaseClass<TSelf> is an implementation detail I'd prefer people not be aware of. So, I want substantiation of ChildClass directly.

I think what you are looking for is the ability to skip having ChildClass entirely when it contains nothing intelligent. Is that the case?

When I do that, I am usually using injection anyway. Do you want this capability without a dependency injection approach (not sure I understand that) or do you want dependency injection thinking built deeper into the language?

Or did I entirely misunderstand your issue?

Kathleen
Sep 21, 2014 at 7:01 PM
Maybe you misunderstood the issue :-)

First of all the language is not supporting context hierarchy chain constraints.

What this pattern generally is good for are the return statements and the where constraints.

These problems accrue if you develop a library for developers with fluent API's which are extensible.

Example:
public class Controller<TSelf>
    where TSelf : Controller<TSelf>
{
    protected virtual TSelf Init()
    {
        // Do some stuff.
    
        return (TSelf)this;
    }
    
    protected virtual TSelf Start()
    {
        // Start the controller.
    
        return (TSelf)this;
    }
}

public class MainController : Controller<MainController>
{
    protected override TSelf Init()
    {
        base.Init();
        
        // Do some extra stuff.
    
        return (TSelf)this;
    }
    
    protected override TSelf Start()
    {
        base.Start();
        
        // Do some extra stuff.
        // Start the controller.
    
        return (TSelf)this;
    }
}


public class Programm
{
    public static void Main()
    {
        var controller = new MainController()
            .Init() // If you do not return the TSelf you have to cast or have to implement a lot more methods and override them.
            .Start(); // With TSelf the virtual call is made to the right class.
    }
}
There also problems if you use lots of "virtual" and "new" modifiers. the behavior is no longer simple to understand.
So it is very easy to make mistakes for those late bounded virtual method calls.

You can do something like that, but this has its own drawbacks:
public abstract class MainController<TSelf> : Controller<TSelf>
    where TSelf : MainController<TSelf>{...}

public sealed class MainController : MainController<MainController> {...}
this can be made much more simple with something like that (just an suggestion).
public class Controller
{
    protected virtual this Init()
    {
        // Do some stuff.
    
        return this;
    }
    
    protected virtual this Start()
    {
        // Start the controller.
    
        return this;
    }
}
(notice the method return type "this").

So such context hierarchy chain constraints can be very useful for extensible libraries.

Did this example gave you an impression what was meant?
Sep 22, 2014 at 6:26 PM
While I recognize the usefulness of allowing a function like Clone to specify its return type as matching the thing upon which it is invoked, such a thing could not be supported without adding some restrictions to virtual types which, while they would be useful, are not supported in the present Runtime.

Most notably, given the code:
public class Foo
{
  public virtual this MakeAnother()  { return new Foo(); }
}
public class Bar : Foo { }
the implementation of MakeAnother in class Foo is legitimate for class Foo, but is not legitimate for class Bar. What's needed here is a means by which a method could behave as virtual for instances of type Foo, but abstract for instances of any type derived from foo. A similar issue arises with constructors: the fact that a concrete type T is inheritable, and wishes to allow anyone anywhere to use a particular constructor to construct instances of T, should not imply that code everywhere in the universe should be able to derive from T and use that constructor to construct instances of types derived from T.

If the Runtime allowed classes to better exercise independent control over the "face" that is shown to the outside world, versus the face derived-class code will inherit, then features like "type of self" would be useful. Unfortunately, I don't think the design of .NET would be particularly amenable to such distinctions.
Sep 22, 2014 at 7:59 PM
"Did this example gave you an impression what was meant?"

Nope, I'm still clueless about what you want.

You use TSelf in your derived class, where it should be MainController, but other than that the code is sensible, and I don't see what is failing.

So, what is it you want C# to do that it is not doing today? Which of the code you propose doesn't work today?

Kathleen
Sep 22, 2014 at 8:06 PM
IOW, why does this not solve your problem?
  public class Bar<T> : BarBase
         where T : Bar<T>
     {
        public virtual T MyMethod()
        { return null; }
     }

     public class Barbacoa : Bar<Barbacoa>
     {
        public override Barbacoa MyMethod()
        {
           return base.MyMethod();
        }
     }
Sep 22, 2014 at 8:18 PM
(at) supercat
that is not the case i wanted.
In your return statement you are creating a new instance obviously it has the same type (Foo) but is not the same reference.
What i was referring to is only return of the reference "this" (return this) other reference returning should be not allowed if you declare a method with return type "this"

So of course the given example will not make sense if you can return new instance references, it will break the hierarchy chain.

So in you example it should only allow return it self.
public class Foo
{
  public virtual this MakeAnother()  { return this; }
}
public class Bar : Foo { }
(at) KathleenDollard

Currently the language is not supporting returning hierarchy chain constraints without declaring such complicated constructs.
You use TSelf in your derived class, where it should be MainController, but other than that the code is sensible, and I don't see what is failing.
If i use MainController as return type, then all other classes which derive from MainController also return the type "MainController" and if they want to return their own type (self) then they have to declare the method with the modifier "new", so it is not an "override". To avoid miss behavior you must declare new methods and redirect them, so the new overloaded method has a chance to be called up, from base class.
Sep 22, 2014 at 8:19 PM
Edited Sep 22, 2014 at 9:26 PM
KathleenDollard wrote:
IOW, why does this not solve your problem?
  public class Bar<T> : BarBase
         where T : Bar<T>
     {
        public virtual T MyMethod()
        { return null; }
     }

     public class Barbacoa : Bar<Barbacoa>
     {
        public override Barbacoa MyMethod()
        {
           return base.MyMethod();
        }
     }

public class MyBarbacoa : Barbacoa
{
    public new MyBarbacoa MyMethod()
    {
       return this;
    }
}
Because Barbacoa is now not fluently extensible!
Sep 22, 2014 at 9:24 PM
Edited Sep 22, 2014 at 9:26 PM
I forgot one thing :-)
public class MainController : Controller<MainController>
{
    protected override TSelf Init()
    {
        base.Init();
        
        // Do some extra stuff.
    
        return (TSelf)this;
    }
    
    protected override TSelf Start()
    {
        base.Start();

        // Init the Contoller
        this.Init();  // important to call the correct overriden method.
        
        // Do some extra stuff.
        // Start the controller.
    
        return (TSelf)this;
    }
}
Notice the call to Init in the Start method.
So hope now it is obvious that calling to the Init method from here (base class) needs an override not an overload.
Sep 23, 2014 at 12:00 AM
So you are essentially proposing syntactic sugar which would say that even though a method foo.bar() has a return type of void, the caller should allow foo.bar().boz() and replace it with (var temp=foo; temp.bar(); temp.baz();)? I guess that would be workable within the Runtime (since it's just syntactic sugar), but it's not as useful as would be having e.g. a Clone method return something inherently of the proper type.
Sep 24, 2014 at 1:06 PM
Edited Sep 24, 2014 at 1:20 PM
No,

what i'm proposing is, the ability to express with a keyword that the return type of an instance method (or property, maybe field) must be the refernce stored in the keyword "this".

And one importend side note is, that it should not be an specific type, the return type must be infered from usage.
Of course it has the limitation that only types of the hierarchy chain can be returned. (But this is intended)


So again an example with the desired modification:
public class Controller
{
    protected virtual this Init()
    {
        // Do some stuff.
    
        return this;
    }
    
    protected virtual this Start()
    {
        // Start the controller.
        
        return this;
    }
}

public class MainController : Controller
{
    protected override this Init()
    {
        base.Init();
        
        // Do some extra stuff.
        // return new MainController() // will result in an compilation exception, because only the this refrence is allowed.
        return this;
    }
    
    protected override this Start()
    {
        base.Start();
        
        // Do some extra stuff.
        // Start the controller.
        this.Init();
        
        return this;
        // Could be also
        // return this.Init();
    }
}


public class Programm
{
    public static void Main()
    {
        var controller = new MainController()
            .Init() // Because you can now override the methods the call is correct.
            .Start(); // The second call is also correct, because the type is infered (MainController) and will not be the type of Controller.
    }
}
Sep 24, 2014 at 3:36 PM
Hrm, any thought as to how that could actually be implemented? The return type of the Init method of Controller would have to either be a type parameter and Controller itself a generic type, or it would need to be a concrete TypeRef meaning that the derived classes would also have a return type of Controller. The former case is probably the only feasible way to achieve that but that would be tricky to support appropriately especially if Controller were already generic and it would make a mess of reflection.

It would be a little messy but you can sort of achieve this today by combining shadowing public methods with internal virtual methods:
public class Controller
{
    public Controller Init()
    {
        InitInternal();
        return this;
    }

    protected virtual void InitInternal()
    {
        // do stuff here
    }
}

public class MainController : Controller
{
    public new MainController Init()
    {
        InitInternal();
        return this;
    }

    public MainController Start()
    {
        // do other stuff here
    }

    protected override void InitInternal()
    {
        base.InitInternal();
        // do more stuff here
    }
}

static class Program
{
    static void Main()
    {
        var controller = new MainController();
        controller.Init() // calls the shadowed Init method on MainController which returns a type of MainController
            .Start();
    }
}
Sep 24, 2014 at 5:48 PM
JavedSaqib wrote:
what i'm proposing is, the ability to express with a keyword that the return type of an instance method (or property, maybe field) must be the refernce stored in the keyword "this".
The return type or the return value? If the return value is required to be this, then is there any reason the function itself would need to return it? I would think a simpler solution would be to say that if the type yielded by expr has a void method foo, and that method is tagged with a [ReturnThis()] attribute, then expr.foo() will be equivalent to (var temp=expr; temp.foo(); temp).

Otherwise, it would be possible to define an interface
interface ISelf<out T> { T Self {get;} }
and have many other generic interfaces include a covariant generic parameter TSelf for the implementing type and inherit from ISelf<TSelf>. Such a design can do many wonderful amazing things (e.g. if a type implements IFoo<ItsOwnTyps>, IBoo<ItsOwnTyps>, and IGoo<ItsOwnTyps>, it will also automatically implement IFoo<IBoo<ItsOwnTyps>>, IBoo<IFoo<ItsOwnTyps>>, IFoo<IGoo<IBoo<IGoo<IFoo<IBoo<ItsOwnTyps>>>>>>>, and all other such types, and a reference to such a type may be successfully cast to any such "composite" interface formed from types it implements. Unfortunately, it's possible for a class Foo from implementing ISelf<Bar> without actually deriving from Bar or implementing any of Bar's interfaces, and in that case a cast from ISelf<Bar> to Bar or any of its interfaces could fail.
Sep 24, 2014 at 11:42 PM
Edited Sep 24, 2014 at 11:43 PM
(at) Halo_Four,

Some times i use what you suggested, and some times is use what i wrote in the first post.
public abstract class BaseClass<TSelf>
    where TSelf : BaseClass<TSelf>
{
    public virtual TSelf Foo()
    {
        // Do something which is only implemented in base class.
        return (TSelf)this;
    }
}
Take a look her what Eric Lippert wrote about it. He discourage its use because of violating the " Liskov Substitution Principle", and i think, if we can add this feature then you are not violating this principle.

http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

http://fernandof.wordpress.com/2007/09/23/recursive-generics-restrictions/
Sep 25, 2014 at 10:04 AM
Edited Sep 25, 2014 at 10:06 AM
(at) supercat

Your first option with attributes does not help, because you have no type safety and no compiler support.
(What i mean with compiler support is, it can not decide on given information how to call the correct virtual method.)

The second option is also not type-safe see Eric Lippert post for more explenations:
http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx
Sep 25, 2014 at 8:46 PM
JavedSaqib wrote:
Your first option with attributes does not help, because you have no type safety and no compiler support.
(What i mean with compiler support is, it can not decide on given information how to call the correct virtual method.)
There's only one virtual method; its return type is void. When that virtual method is used as an expression, its compile-time return type will be the same as the instance upon which it's invoked, and the return value will always be or identify the instance upon which it is invoked. Languages which support the attribute would have compiler support. If a particular language has not been updated to support the feature, code would have to invoke the void function and then separately use the thing upon which it was invoked, but any new feature would have that problem since there is presently no means to encode in CIL a constraint which, if met by a generic type, would imply that a particular method will return the same type as the thing upon which it is invoked, and thus nothing that any compiler could generate which would cause code to magically have the desired behavior in other, existing, compilers.

As for type safety, what isn't type-safe about a compiler assuming that an expression which fits in a particular type for purposes of invocation will fit in an expression of that same type?