0

I have such problem: the interface with no in and out parameters.

public interface IIndexer
{
    void FillIndex();
}

It should work differently for different kinds of activities( comments, likes and so on). And when I apply IoC principle through dependency injection (via Ninject), my resolver cannot resolve me CommentsIndexer, LikesIndexer cause I try to inject them as IIndexer type.

I have two solutions on my mind :

  1. Deriving a lot of interfaces from IIndexer for every activity and then implement each of them in my classes ( this option clutters code with many empty interfaces)
  2. Use generics to differentiate them

_

public interface IIndexer<T>
    where T : Activity
{
    void FillIndex();
}

But in this case IDE says me that Type parameter T is never used

The question is : is it OK to use generics in cases like this (for type distinguish only)? Thank you!

14
  • 1
    Why not just use concrete indexer class as dependency? DI doesn't require the dependency to be an interface.
    – Euphoric
    Commented Sep 5, 2017 at 7:43
  • 5
    or avoid void DoSomething() black holes of type erased side-effect
    – Caleth
    Commented Sep 5, 2017 at 8:23
  • 8
    Then don't use a framework; use pure DI and/or code your own resolver and factories.
    – David Arno
    Commented Sep 5, 2017 at 8:58
  • 5
    No, they really aren't. If you use a framework, you are coupled to it. I can understand your desire to only lightly couple to it, but then you run into problems like this. Perhaps another option for you would be to hide the container behind an abstraction layer. That way, if you change your mind on which framework to use in future, then you need only change the functionality of that layer; not of your whole app.
    – David Arno
    Commented Sep 5, 2017 at 9:14
  • 3
    Easy answer. No, it's not good idea. That's not what generics were made for. You won't solve a design flaw with another design flaw.
    – Laiv
    Commented Feb 2, 2018 at 20:53

2 Answers 2

0

But in this case IDE says me that Type parameter T is never used

In most cases, it is indeed very weird to declare a generic type and then not use it. However, there is a potentially meaningful reason here.


Suppose you have a single class that, for some reason, indexes both likes and comments (this is not always a violation of SRP, though it does seem that way in this simplified example)

With your non-generic example, you'd run into an issue:

public class CommentsAndLikesIndexer : IIndexer
{
    public void FillIndex()
    {
         Console.WriteLine("index filled!");
    }
}

var myIndexer = new CommentsAndLikesIndexer();

There's no way to signal that this can be used for the indexing of both comments and likes. There's also no way to only index comments (or likes) if both are implemented (other than some unintuitive boolean properties that go outside of the interface itself).

There is also no way to figure out what myIndexer is capable of indexing.


When you use a generic interface, you can specify which entities can be indexed:

public class CommentsAndLikesIndexer : IIndexer<Comment>, IIndexer<Like>
{
    public void FillIndex()
    {
         Console.WriteLine("index filled!");
    }
}

var myIndexer = new CommentsAndLikesIndexer();

Now, we have a way of testing what can be indexed by myIndexer:

bool isLikeIndexer    = myIndexer is IIndexer<Like>;
bool isCommentIndexer = myIndexer is IIndexer<Comment>;

However, we still run into the issue where FillIndex() will be used for both cases.

Maybe that's what you want. But let's assume that you want them to be separate. You can achieve this by using explicit interface declarations:

public class CommentsAndLikesIndexer : IIndexer<Comment>, IIndexer<Like>
{
    public void IIndexer<Comment>.FillIndex()
    {
         Console.WriteLine("comment index filled!");
    }

    public void IIndexer<Like>.FillIndex()
    {
         Console.WriteLine("like index filled!");
    }
}

And now we have separate implementations for a doubly implemented interface!

Some sidenotes:

  • The downside is that when an interface is explicitly implemented, you must always use the interface as the variable's type, to clarify which method you want to use.
    • CommentsAndLikesIndexer myIndexer = new CommentsAndLikesIndexer(); myIndexer.FillIndex(); does not work. The compiler doens't know which method you want.
    • IIndexer<Comment> myIndexer = new CommentsAndLikesIndexer(); myIndexer.FillIndex(); does work.
    • By extension, CommentsAndLikesIndexer myIndexer = new CommentsAndLikesIndexer(); (myIndexer as IIndexer<Comment>).FillIndex(); would also work.
  • If your method signature would be unique based on the generic parameter (e.g. void FillIndex(T myObj)), then you could do this without explicit interface implementation, since the two methods will not conflict.

Is it OK to use generics in cases like this (for type distinguish only)?

The above being said, a generic interface that doesn't use its generic type parameter is an unusual thing to see. There are fringe use cases for it, but I would strongly suggest doublechecking if this is the best approach for you.

If your method is indeed correct as it is intended (no input parameters), then this can be a valid approach.

0

If it is an event, you should treat it as such. Create an observer/subscriber pattern where the caller passes an EventHandler interface for a specific event and when that event happens, calls any and all EventHandlers. To differentiate events, you could pass a EventArgs class with information relative to the event in question, albeit initially only the event name, but you could easily expand on that later.

Alternatively you could simply create an event delegate for each event type.

In either case, the class that triggers the event knows nothing of the class which performs the task, which is what you want. You don't necessarily have to use an dependency injection library like Ninject for this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.