0

As far as I know, the following should just work since TThis is literally the same thing as Data<TThis, TActor> but when I try to pass it to a method of TActor the compiler complains.

public class Data<TThis, TActor>
    where TThis : Data<TThis, TActor>
    where TActor : Actor<TThis>
{
    public TActor actor;

    public Data(TActor actor)
    {
        this.actor = actor;
        actor.Initialize(this); // Complains: Argument Type 'Data<TThis,TActor>' is not assignable to parameter type 'TThis'
    }
}

public class Actor<TData>
{
    public TData data;

    public void Initialize(TData newData)
    {
        data = newData;
    }
}

public class MyActor : Actor<MyData> {}

public class MyData : Data<MyData, MyActor>
{
    public MyData(MyActor actor) : base(actor) {}
}

EDIT: casting this to TThis before passing it to actor.Initialize seems to work, but I don't see why that should be necessary.

theo
  • 305
  • 2
  • 7
  • 1
    `TThis` is not the same thing as `Data`, it's restricted to be a type inheriting the `Data` – Pavel Anikhouski Feb 02 '20 at 16:34
  • Pavel is correct. Your statement "the following should just work since `TThis` is literally the same thing as `Data`" is simply false. **A constraint is not treated as an identity**. In your case the constraint is not an identity at all; we can easily construct a program where we can see that there are types that meet the constraint that are not identical. But even where we put additional constraints that *do* make the constraint an identity, the language's type system does not deduce that so you cannot make use of that fact. – Eric Lippert Feb 03 '20 at 19:13

1 Answers1

3

In case it is not clear why your program is illegal, let me rename your classes:

public class Cage<C, A>
    where C : Cage<C, A>
    where A : Animal<C>
{
    public M(A animal)
    {
        animal.SetCage(this); // Why is this illegal?
    }
}
class Animal<C>
{
  public void SetCage(C c) {}
}
class Fish : Animal<Aquarium> {}
class Aquarium : Cage<Aquarium, Fish> { }

The question is: why is animal.SetCage(this); illegal? Well, to be legal that line has to be type safe for every possible legal construction of Cage and Animal. That's what it means for a type safety system to be sound; you don't get to be type safe just some of the time.

Can we construct a scenario where we have legal Cage and Animal types but the call to animal.SetCage() is wrong? We certainly can.

class SharkTank : Cage<Aquarium, Fish> { }

Obviously this is legal; Cage<Aquarium, Fish> was a legal base type for Aquarium so it should be a legal base type for any type!

Now what happens when we call:

var s = new SharkTank();
var f = new Fish();
s.M(f);

s.M(f) calls Fish.SetCage(this) which expects an Aquarium. But this is not of type Aquarium! this is of type SharkTank, which cannot be converted to Aquarium. SharkTank and Aquarium have the same base type, and two different class types that have the same base type are not implicitly convertible to each other.

What you are trying to do here is create a constraint that means "the generic type is guaranteed to be constructed with its own type as a type parameter" but there is no way to express that constraint in C#. Instead of tying yourself in knots like this creating recursively defined types with constraints that refer to themselves, make your type system a lot simpler. Whatever constraint it is that you are trying to impose can be imposed by runtime policy rather than attempting to capture it in the type system. The type system is not that strong! Don't try to make it enforce rules it was not designed to enforce.

If this subject interests you, see my articles about this pattern specifically:

https://ericlippert.com/2011/02/02/curiouser-and-curiouser/

And about the general problem of trying to put too many rules into the type system:

https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    Am I crazy or did all the generic type parameters get stripped out of the code snippets in the "Curiouser and curiouser" article? – Ryan M Feb 04 '20 at 14:47
  • @RyanM: Oof, thanks for noting that. The dumb editor I use sometimes gets confused about what is an HTML tag. Will fix. – Eric Lippert Feb 04 '20 at 15:20