If I'm not mistaken, what you want to do (wishing you could do) looks something like this:
class ProductController<T> : Controller where T : ProductController { ILogger<T> _logger; ... etc }
I think you can get a pretty flexible interface if you digress a bit from your design. Here you have three parts: the controller, the registrar, and the controller and registrar object, which I will call the data transfer object. So, you have a "product controller" and a "product registrar" (which you are currently calling the "product controller registrar").
Say you have this structure of DTO objects:
public class DataTransferBase { } public class Product : DataTransferBase { }
Now, instead of writing a log about yourself with the controllers, why not have a logger and controller, both belong to the DTO? So, the logger looks like:
public interface ILogger { void Log(string message); } public interface ILogger<T> : ILogger where T : DataTransferBase { void Log(T item); } public class FileLogger<T> : ILogger<T> where T : DataTransferBase { public virtual void Log(T item) { } public void Log(string message) { } }
... and the controller is like:
public interface IController<T> where T : DataTransferBase {} public class Controller<T> : IController<T> where T : DataTransferBase { /// <summary>Initializes a new instance of the ProductController class.</summary> public Controller(ILogger<T> logger) { } public virtual List<T> GetItems() { return new List<T>(); } }
Now you have a log that will work on any DTO controller and a controller that will work on any DTO, and this controller will take as a constructor parameter a registrar that will work on the same DTO as it is. Now you can have more specific implementations if you want:
public class ProductFileLogger : FileLogger<Product> { public override void Log(Product item) { } }
and
public class ProductController : Controller<Product> { /// <summary> /// Initializes a new instance of the ProductController class. /// </summary> public ProductController(ILogger<Product> productLogger) : base(productLogger) { } public override List<Product> GetItems() { return new List<Product>(); } }
And you can connect them specifically or in general, as you wish:
public class Client { private void DoSomething() { IController<Product> myController = new ProductController(new ProductFileLogger());
Please note that I just whipped it on the fly, so this may not be optimal, but I'm just trying to convey a general idea. You cannot declare a class with the generic type itself (as far as I know), but you can have two interaction classes (controller and registrar) that work with the same generic type. That is, an IController can own an ILogger, and when you instantiate an IController, you force its logger to work on the same type.