1

I have recently stumbled upon a use case of extension methods and wanted to gather some design thoughts.

The problem: A IEnumerable of a POCO and within that list there should exist a POCO that satisfies a condition say POCO.IsTheRightOne == true.

You could write and extension class for this query

public static class POCOExtensions
{
   public static bool GetIsTheRightPOCO(this List<POCO> source){
      return source.Single(poco => poco.IsTheRightOne == true).UsefulProperty;
   }

}

Now this to me is clean. It is easily testable, their is no need for a utils or helper class to be in the middle, the logic is encapsulated, and no side effects or other classes are needed.

Another way I could go about this is create a POCOList class and wrap the behavior in that and then you would get the same benefits and still have the ability to override behavior if needed. But since this is a poco class chances of me needing that are pretty slim.

Something about this pattern though just doesn't sit right with me. I feel like I'm paying a unseen cost. What other drawbacks to this design am I missing?

2 Answers 2

2

Extension methods are Bad(tm)

  1. Static methods cant be mocked out

    Sure you can test this method, but can you unit test something that uses it?

  2. Extension methods are hard to find.

    Unless you know the namespace and package to include for this method you have no way to find it. As opposed to a method on the Poco itself or a service for the over all operation.

  3. It has all the downsides of a helper library.

    What you have here is a orphaned helper method, It's hard to name, it's hard to group up with the data or other object that need it, its hard to know what references it and what will break if you change it.

Instead, I would suggest this should be a private method on a service class which does whatever larger operation you are performing with your Pocos. That encapsulates it with other methods and data related to the process and gives you a nice DoWhateverWithPocosService() class which can be interfaced and mocked as required.

10
  • Regarding #1, it depends on the static method. If the method is idempotent, then yes, you can write unit tests for things that call the static method. Regarding #2: it depends. If the extension methods are in the same namespace as the type it extends, then they are just as easy to find. The OP's use case is a little complicated because the proposed method extends List<T>, so you have two types requiring using statements before you can discover the method. But #3 is spot on. Mar 23, 2023 at 14:02
  • #1 if you are being strict your unit test wants to call a mocked version, which the static doesn't allow. #2 if you use the same namespace you are going to collide with other extension methods
    – Ewan
    Mar 23, 2023 at 14:23
  • 3
    Mocking is not a requirement for "strict" unit testing. A "unit" of behavior is not a well-defined term, but typically you mock behavior that modifies or reads from external state. If the logic is simple and idempotent, why complicate your test with a mock? And I wouldn't worry about name collisions for domain-specific extension methods. Sure it is a risk, but it is a judgement call whether or not a naming collision is likely. Mar 23, 2023 at 14:53
  • I guess the question is do you know the call doesn't have side effects. If its just some pure function obvs its going to be fine, but if its some random library extension method? Same with the namespaces, sure if its all your code then you aren't going to worry, but If you are publishing services.AddWebAuthentication(config) ?
    – Ewan
    Mar 23, 2023 at 15:00
  • 1
    I would say, look at the bigger picture. What happens when you have more than one way to select the right poco? what happens when this function is reused all over the place and then you want to change it for one particular edge case? You can define stand alone functions all over the place if you like, but you lose the structure of OOP
    – Ewan
    Mar 23, 2023 at 19:32
1

My litmus test whether to use extension methods - or not - are the following two criteria:

  1. One cannot easily add the method to the extended type directly, because it would require changing a library outside of ones control, or add an undesired dependency to the library where the type is defined.

    List<T> is part of the framework, so clearly out of your control. But as you said, you could derive POCOList from List<POCO> and hence create a type which you can extend.

  2. The added method "fits well" to the abstraction of the type which gets extended.

    By "fits well", I usually think of methods which are not exclusively supporting just one specialized use case, but make sense for the type itself, often with some potential to be reused in different use cases (at least in the context of the application or system where the extension method "lives"). Note this is also a criterion I would expect to be fulfilled for a derivation like POCOList.

Sure, the second criterion may be somewhat opinionated, but the way you described, it seems this is not fulfilled here. Hence I think Ewan's suggestion to place a method in the outer context (like some service) is ok here.

Where I strongly disagree to that (otherwise good) answer, however, is that extension methods are "generally bad". Such statements are quite unprofessional and too dogmatic for my taste.

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.