-3
\$\begingroup\$

I understand that following the dependency injection principle, a class should ask for abstractions in the constructor and should not create them itself. This way they are not dependent on concrete types and this means loosely coupled code which we all love.

I was thinking about using function delegates (Action and or Func) instead of interfaces as it essentially accomplishes the same thing.

public class ReverseUpper
{
    private Func<string, string> __upper;
    public ReverseUpper(Func<string, string> upperService)
    {
        __upper = upperService;
    }
    public string ToReverseUpper(string input)
    {
        return new string(__upper(input).ToCharArray().Reverse().ToArray());
    }

}
public class Upper
{
    public string ToUpper(string input)
    {
        return input.ToUpper();
    }
}

and the composition root:

var upper = new Upper();
var reverseUpper = new ReverseUpper(upper.ToUpper);

I haven't found anyone who talks about this way of doing it and I don't find any obvious disadvantage to it. What are your thoughts?

Advantages:

  • there is no need to define an interface for each level
  • it is much easier to see for each class the exact function that it depends on
  • interface segregation
\$\endgroup\$

closed as off-topic by Nikita B, pacmaninbw, alecxe, t3chb0t, Graipher Jun 5 '17 at 14:30

This question appears to be off-topic. The users who voted to close gave this specific reason:

If this question can be reworded to fit the rules in the help center, please edit the question.

  • 5
    \$\begingroup\$ Is that a real code? \$\endgroup\$ – Denis Jun 5 '17 at 1:15
  • 3
    \$\begingroup\$ UpperServicescreams "example code" to me. IMHO, you should either add some context on when would you actually use this code and what problem ReverseUpper solves, or replace it with real code. \$\endgroup\$ – Nikita B Jun 5 '17 at 10:00
1
\$\begingroup\$

It's not unheard of, and your example looks fine to me. However, problems may arise when you have a method with multiple arguments (now you've lost context since you don't have an indication given via argument names). You also may have issues in the future where you'll slowly be tacking on more and more methods/configuration to the constructor. Of course; if that does happen, it's not too difficult to extract it out into an interface.

In order to tackle the first problem; I'd recommend using a delegate as an argument, rather than Action/Func. For example:

public class ReverseUpper
{
    public delegate string upper(string input);

    private upper __upper;
    public ReverseUpper(upper upperService)
    {
        __upper = upperService;
    }
    public string ToReverseUpper(string input)
    {
        return new string(__upper(input).ToCharArray().Reverse().ToArray());
    }
}
\$\endgroup\$
  • \$\begingroup\$ can you please indicate why you are suggesting a delegate instead of action/Func? \$\endgroup\$ – CodingYoshi Jun 5 '17 at 12:56
  • \$\begingroup\$ @CodingYoshi It allows you to name arguments. Note that you can still pass in any method as an argument, assuming the types match, including Func and Action \$\endgroup\$ – Rob Jun 5 '17 at 12:57
  • \$\begingroup\$ Very late update, but I was originally incorrect. One may write new ReverseUpper(a => a), but cannot pass in a Func<string, string> as an argument. It must be wrapped in the delegate. For example, new ReverseUpper(new ReverseUpper.upper(myFunc)); \$\endgroup\$ – Rob Feb 26 '18 at 11:50
2
\$\begingroup\$
public class Upper
{
    public string ToUpper(string input)
    {
        return input.ToUpper();
    }
}

var upper = new Upper();
var reverseUpper = new ReverseUpper(upper.ToUpper);

Using delegate injection is sometimes useful and I do it myself occasionally but in your example it doesn't make any sense. You already have a class that provides string manipulation functions (or at least it looks like it could) and all you need is to add an interface to it but instead you pick just one method. It would be fine if those method were extensions but here it's an instance method that actually could be static because it does not require any state.


Another issue is that the name of the class already suggests a concrete implementation of the delegate passed, this is, ToUpper. But what if I passed it ToLower? I would completely change what your class does, even though its name says something completely different. In this case it would be safer to use an interface that provides a ToUpper method that your class calls because then you can better verify it does what it should and if someone changes the implementation of ToUpper its his problem.

This would be for the case that your class really only should do ToUpper but if you still want to use delegates then you should rename the class to something like CaseChanger or StringTransformation etc. where you can specify either ToUpper or ToLower and either one would be valid.

Just saying Func<string, string> is very ambiguous for this case and I would expect to pass anything that consumes a string and produces one, be it String.Replace or Regex.Replace or anything else with a valid signature. If it's acceptable to specify anything of that kind then it's fine, otherwise use an interface so that you don't have to complain that the apparently expected ToUpper delegate replaces a with -. To me it's just string to string and I don't care what its exact implementation should be.


So the general rule of thumb is: you can use a delegate if anything of that signature is acceptable (string in --> string out) and an interface where you expect some concrete functionality ToUpper or ToLower etc.

\$\endgroup\$
  • \$\begingroup\$ great idea. never thought writing my own delegate instead of using action or func would be useful. thanks. \$\endgroup\$ – Vasilescu Andrei Jun 5 '17 at 10:09
1
\$\begingroup\$

Delegates and Interfaces are meant to accomplish different goals. Although in some cases they seem interchangeable, care must be taken to decide which to use. Here are a few things to consider:

  • Interfaces can have more than 1 method. They can have properties and events as well.
  • Interfaces act as a blue print and help implementors design things. For example, if I want to create collection, the IList interface has many methods that will guide me. So long as I implement most of those methods, I will have a useful collection class.

Having a delegate for all the methods of IList would not be ideal. It would also be hard to see a relationship between them. Having them all in one interface is good because it shows they are related and it describes what a list is.

Delegates on the other hand are useful when the implementation can change during runtime. For example, if you look at the many methods of Linq, they are delgates. Why? Because the implementation can change during runtime. If I am using the Where method, in one case I may need to find all employees with Id of 7, in another case I may need all male employees and so on. Having an interface would not be ideal because I would need to create a class which implements all the different cases.

Now let's consider something like IComparable or IComparable<T>. I am sure the framework designers had to ask whether to go with delegates or an interface. They went with interface. That is a good decision because in most cases the comparison does not change at runtime. Therefore, one implementation is enough.

Search for C# interfaces vs delegates for more info. MSDN also does a good job.

\$\endgroup\$
-1
\$\begingroup\$

I've become a big fan of functional programming styles over the years. With them there's no need for interfaces, because the contract is embedded in the function or action automatically. Simply stated, you can't use this function or action unless you follow what it tells you to do. The number of parameters is unlimited no problem there. To me it's much cleaner and accomplishes everything an interface does!

There are so many however, that grew up on interfaces that they are literally automatically rejecting functional styles. I've posted many things to Code Project only to be laughed off their site! With Async and Functional styles, you don't need MVVM any longer. But try to tell that to an MVVM lover.

\$\endgroup\$
  • \$\begingroup\$ I've never liked those holy patterns so I'm curious about your articles on Code Project and the alternative solution. Could you post a link? \$\endgroup\$ – t3chb0t Jun 5 '17 at 7:06

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