2

I want two generic classes to be able to reference each other. I can't seem to get anything to compile. Tried this:

class Program
{
    static void Main(string[] args)
    {
    }

    public class ClassA<BT> where BT: ClassB<ClassA<BT>>
    {
        BT btvar;
    }

    public class ClassB<AT> where AT: ClassA<ClassB<AT>>
    {
        AT atvar;
    }
}

This has a practical implementation, but I wanted to avoid a complicated explanation of my own code. I can create closed classes that obey the rule, I just can't seem to describe a generic class or interface for those closed instances.

Rich Oliver
  • 6,001
  • 4
  • 34
  • 57

5 Answers5

5

As fas as I understand, this is impossible, and this is why:

You want A, with a template value of type B. You want B, with a template value of type A.

If you create a new instance of A, the compiler has to check of T is of type B. To check if it's type B, it has to check if B is of type A, A of type B, etc etc.

You end up creating an endless loop.

Aaron McIver
  • 24,527
  • 5
  • 59
  • 88
Michiel van Vaardegem
  • 2,260
  • 20
  • 35
  • It turns out that it is possible. I posted the solution since this question is one of the first that shows up on Google. It is correct that the declaration can't be recursive. You need to derive the class for it to work. – sbsmith Nov 02 '17 at 16:08
2

The way I ended up doing it was by adding the class as one of its own type parameters. It's not too pretty, but it works.

public abstract class Saver<TSaver, TData>
    where TSaver : Saver<TSaver, TData>
    where TData : ISaveable<TData, TSaver>
{ ... }

public interface ISaveable<TData, TSaver>
    where TData : ISaveable<TData, TSaver>
    where TSaver : Saver<TSaver, TData>
{ ... }

public class WorkspaceWindow : ScalingWindow, ISaveable<WorkspaceWindow, WorkspaceWindowSaver>
{ ... }

public class WorkspaceWindowSaver : Saver<WorkspaceWindowSaver, WorkspaceWindow>
{ ... }
hypehuman
  • 1,290
  • 2
  • 18
  • 37
1

This is possible, the following is based on the answer to this question.

public class ClassA<BT, AT> :
    where BT : ClassB<AT, BT>
    where AT : ClassA<BT, AT>
{
    BT btvar;
}

public class ClassB<AT, BT> :
    where BT : ClassB<AT, BT>
    where AT : ClassA<BT, AT>   
{
    AT atvar;
}

You won't be able to use the classes directly, you'll need to override them.

public ClassAImp : ClassA<ClassBImp, ClassAImp>
public ClassBImp : ClassB<ClassAImp, ClassBImp>

So you may as well make ClassA and ClassB abstract.

sbsmith
  • 600
  • 1
  • 5
  • 16
0

this will compile, but I would like to see you instantiate either ClassA or ClassB:

    public class ClassA<TBt>  where TBt : ClassB<TBt>
    {
        TBt _btvar;
    }

    public class ClassB<TAt> : ClassA<TAt> where TAt : ClassB<TAt>
    {
        TAt _atvar;
    }
mindandmedia
  • 6,800
  • 1
  • 24
  • 33
  • No well that doesn't answer the problem because each constraint is to a self referencing class, not a back referencing class. – Rich Oliver Feb 29 '12 at 18:52
0

"Why would you want to?" sounds like a good question to me. The point of Generics it to allow you to abstract a class to allow it to use multiple types. If the constraint limits the type to a concrete type, you are only allowing the type and its subclasses. If you aren't doing this for subclasses, don't use generics. If you are, how about using an interface?

public interface IClassA<ITB> { }

public interface IClassB<ITA> { }

public class ClassA<AT,BT> : IClassA<BT> where BT : IClassB<AT>
{
    BT btvar;
}

public class ClassB<BT,AT> : IClassB<AT> where AT : IClassA<BT>
{
    AT atvar;
}

public class ClassADerivedClosed : ClassA<ClassADerivedClosed, ClassBDerivedClosed> { }

public class ClassBDerivedClosed : ClassB<ClassBDerivedClosed, ClassADerivedClosed> { }
foson
  • 10,037
  • 2
  • 35
  • 53