Home > Software design >  How to resolve circular referencing in convoluted C# generics setup?
How to resolve circular referencing in convoluted C# generics setup?

Time:10-14

In order to save code, I tried setting up something like this:

namespace Base {
    class Application<CCOMPONENT1, CCOMPONENT2>
        where CCOMPONENT1 : Component1
        where CCOMPONENT2 : Component2 {
        public CCOMPONENT1 component1;
        public CCOMPONENT2 component2;
    }
    class Component<CAPPLICATION>
        where CAPPLICATION : Application {
        public CAPPLICATION application;
    }
    class Component1<CAPPLICATION> : Component<CAPPLICATION>;
    class Component2<CAPPLICATION> : Component<CAPPLCIATION>;
    ...
}
namespace Derived {
    class Application : Base.Application<Component1, Component2>;
    class Component1 : Base.Component1<Application>;
    class Component2 : Base.Component2<Application>;
    ...
}

Obviously, it's not working. When I specify the generic component types for the Application class, I have to specify the Application class as generic type, which in turn requires me to again specify the generic component types, and so on. Very much like a circular reference.

What I'd like to achieve is this: The Base.Application class shall contain references to Base components only, and the Base.Component classes shall contain a reference to the Base application only. However, the Derived.Application class shall contain references to Derived components, and the Derived.Component classes shall contain a reference to the Derived application.

Although I have played around with generics for quite a while, I still can't wrap my head around this problem. Is this even possible at all? Are there any workarounds or maybe even cleaner approaches to what I'm trying to do?

CodePudding user response:

With the pointers I got from the comments, I figured it out:

namespace Base {
    public class Application<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TAPPLICATION : Application<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TCOMPONENT1 : Component<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TCOMPONENT2 : Component<TAPPLICATION, TCOMPONENT1, TCOMPONENT2> {
        public TCOMPONENT1 Component1;
        public TCOMPONENT2 Component2;
    }
    public class Component<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TAPPLICATION : Application<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TCOMPONENT1 : Component<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TCOMPONENT2 : Component<TAPPLICATION, TCOMPONENT1, TCOMPONENT2> {
        public TAPPLICATION Application;
    }
    public class Component1<TAPPLICATION, TCOMPONENT1, TCOMPONENT2> : Component<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TAPPLICATION : Application<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TCOMPONENT1 : Component1<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TCOMPONENT2 : Component2<TAPPLICATION, TCOMPONENT1, TCOMPONENT2> {
    }
    public class Component2<TAPPLICATION, TCOMPONENT1, TCOMPONENT2> : Component<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TAPPLICATION : Application<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TCOMPONENT1 : Component1<TAPPLICATION, TCOMPONENT1, TCOMPONENT2>
        where TCOMPONENT2 : Component2<TAPPLICATION, TCOMPONENT1, TCOMPONENT2> {
    }
}
namespace Derived {
    public class Application : Base.Application<Application, Component1, Component2> {
        
    }
    public class Component1 : Base.Component1<Application, Component1, Component2> {
        
    }
    public class Component2 : Base.Component2<Application, Component1, Component2> {
        
    }
}

The key was to supply all generic types to all classes in the Base namespace. Looks a bit convoluted, but I can easily exchange components as I see fit in the Derived namespace, and in the I end I got exactly what I wanted.

Thanks!

  • Related