3

NOTE: I realize that this question is similar to this SO thread, but I am asking about a specific case here, so please don't close this question as a duplicate of that one.

I'm trying to make my C# more functional, and have been reading Functional Programming in C# by Enrico Buonanno. One of the techniques he shows is a Try class, that allows you to handle exceptions gracefully. You can see the code for the Try class here.

This is all very neat, but in a real app, I would want to log exceptions. As his class is static, I don't see a way to do it. As explained in the linked SO question above (amongst others), injecting into a static class either can't be done or isn't a good idea.

The only options I can see are:

  1. Requiring the consuming code to create an instance of the Try class (not currently possible with his code), as opposed to calling the static method. This makes the code a lot less clean

  2. Adding a static ILogger property to the Try class, and setting it before using the class. I don't want to do this, as the Try class will be used on both the client and server in my application, and the logger would be different in the two cases

  3. Adding an ILogger parameter to the class methods. I don't like this for the same reasons as in 1.

  4. Accessing the dependency resolver inside the try class and getting an instance of the logger. This would mean that the code that uses Try would also end up logging to the file, which is not a good idea.

Anyone any suggestions? The approach seems so clean and neat, but it also seems to be missing an obvious basic function.

Keyur Ramoliya
  • 1,900
  • 2
  • 16
  • 17
DreamingOfSleep
  • 1,208
  • 1
  • 11
  • 23
  • Show some code to better demonstrate the current situation and then show the desired behavior. Should help in identifying the problem and possible a solution. – Nkosi Jun 25 '18 at 15:40
  • 3
    Why do you think “injecting into a static class either can't be done or isn't a good idea”? That's the standard way to solve this; pass an ILogger to your class and create a mock ILogger in your unit test that does nothing. If you're running into problems perhaps you shouldn't be using a static class? – Dour High Arch Jun 25 '18 at 15:56
  • 4
    This is snake oil, never pretend that you can handle every exception. Logging must always be an optional feature of an app, in a unit test you'd switch it off or have it write to a specific place. Look how .NET does it with the Trace class (separates the source from the listener). And strongly consider a logging library like NLog or Log4Net. – Hans Passant Jun 25 '18 at 16:04
  • 1
    Do you remember [that one time when Enrico created method signatures whose first parameter was an `ILogger`](https://github.com/la-yumba/functional-csharp-code/blob/master/Examples/Chapter03/Instrumentation.cs)? This would allow you to test static methods by providing a mock `ILogger`. – Dan Wilson Jun 25 '18 at 16:11
  • @DourHighArch The impression I got from that linked question was that it wasn't a good idea. Anyway, as I said, in my case it's a problem as the `Try` class would be in a project that's shared between the client app and server, so I would need two different instances of the logger. Without adding two `ILogger` properties (which is getting very messy), I don't see how I could inject anyway. – DreamingOfSleep Jun 25 '18 at 16:22
  • @HansPassant Who said I was handling every exception? This is just a functional version of a regular `try/catch` block. You'd want to log there wouldn't you? Same here. – DreamingOfSleep Jun 25 '18 at 16:23
  • @DanWilson Oh, I forgot about that! Yes, it can be done that way, but the extra parameter adds noise to the call. I was hoping there was a better way to do it. – DreamingOfSleep Jun 25 '18 at 16:25

1 Answers1

2

Why not have the static method create an instance of the class, as then call a private instance method that does the work? I'm not familiar with Try, but you could do something like this (warning - air code!)...

public class Try<T> {
  // Assuming NInject, but same applies to any other container
  [Inject]
  ILogger Logger { get; set; }

  private Try() {
    // stops anyone from outside this project from creating an instance of Try
  }

  public static Exceptional<T> Do(Action f) {
    Try<T> t = new Try<T>();
    return t.DoPrivate<T>(f);
  }

  private Exceptional<T> DoPrivate<T>(Action f) {
    // This is an instance method, so has access to the injected logger
    try {
      return f();
    } catch (Exception ex) {
      Logger.Log(ex.Message);
      return ex;
    }
  }
}

Does that help? As I said, I'm not familiar with his code, so you may need to tweak it a little, but I think that will give you what you want.

Avrohom Yisroel
  • 8,555
  • 8
  • 50
  • 106