Я недавно работал над чем-то похожим и придумал несколько более простое решение вашей проблемы (хотя и немного слабее).
Что должно быть достаточно, так это привязать универсальную реализацию (DefaultService) к универсальному интерфейсу и конкретные реализации (FooService, BarService) к конкретным интерфейсам. Когда вы запрашиваете конкретный экземпляр интерфейса, Ninject решает, определили ли вы конкретную привязку. Если вы это сделали, он дает вам соответствующий экземпляр, в противном случае он переходит к общей привязке. Следующий код должен сработать.
var kernel = new StandardKernel();
kernel.Bind(typeof(IService<>)).To(typeof(DefaultService<>));
kernel.Bind<IService<Foo>>().To<FooService>();
kernel.Bind<IService<Bar>>().To<BarService>();
EDIT:
Концепция работает на протяжении всего Ninject, поэтому вы можете использовать ее вместе с Extensions.Conventions.
например определите следующее:
public class Foo{}
public class Bar{}
public class Dog{}
public interface IService<T>{}
public class DefaultService<T> : IService<T>{}
public class FooService : IService<Foo>{}
public class BarService : IService<Bar>{}
Используйте соглашения для привязки услуг:
kernel.Bind(x => x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom(typeof(IService<>))
.BindSingleInterface());
и создайте и проверьте соответствующие сервисы:
Assert.IsInstanceOf<BarService>(kernel.Get<IService<Bar>>());
Assert.IsInstanceOf<FooService>(kernel.Get<IService<Foo>>());
Assert.IsInstanceOf<DefaultService<Dog>>(kernel.Get<IService<Dog>>());