0

Working on WPF (UI programming) in C# I've started frequently injecting methods into the constructor of my view models.

For example:

public class HtmlRegexListViewModel : ViewModel
{
   private readonly Action<HtmlRegex> onSelection;

   public List<HtmlRegex> HtmlRegexes { get; private set; }

   private HtmlRegex selected;
   public HtmlRegex Selected 
   {
      get { return selected; } 
      set 
      {
         selected = value;
         onSelection(value);  
      }
   }

   public HtmlRegexListViewModel(Action<HtmlRegex> onSelection, IDatabase database)
   {
      this.onSelection = onSelection;
      HtmlRegexes = database.LoadHtmlRegexesVerySlowly();
   }
}

Now I do this chiefly on view models because I don't want to go to the effort of creating an EventHandler and EventArgs derived pair of classes every time I want to tell the parent object that my state has changed.

However I also use this extensively elsewhere.

It seems like it's a style I'm going to regret when it comes to maintaining the code in future, but at the moment I can't think of many downsides. It decouples, preserves separation of concerns and is a nice way to make a class which has a single responsibility.

So is this a pattern (deferred double dispatch?), an anti-pattern or if it isn't, should it be?

3 Answers3

1

Constructor-injecting functions isn't an anti-pattern in and of itself. And, to the contrary, if you want to be dogmatic about it, your constructor should demand everything an instance needs to be valid. (Though, usually nothing beyond what's needed for validity.)

That said, be sure to consider the prevailing dogmas for what they are: Guidelines for producing simpler, DRY-er, more maintainable code. And then think about how those rules should be manifested or ignored in achieving those end-goals in your application.


In your case, I'm not sure your constructor is the right place for that function injection. If your HtmlRegexListViewModel could be valid without the event/notification, I would personally prefer to construct it like this:

var o = new HtmlRegexListViewModel(database);
o.onSelection = () => { whatever(); };

Or this:

new HtmlRegexListViewModel(database) {
  onSelection = () => { whatever(); }
}

Or whatever.

svidgen
  • 15,252
0

I would not call it "Constructor method injection", because it is not a method, it is a delegate, and delegates are a bit too technical, and a bit too language-dependent: C# supports them, many other languages do not. We could get into an argument about the relative merits of delegates vs. single-method interfaces, but it would be moot point; instead of focusing on the technicalities of that constructor argument, I would rather focus on its purpose, which is observing events. So, I would call it "Constructor observer injection".

I have never heard of such a thing as a pattern, but I do not think it is an anti-pattern, either. You are simply creating an object which offers an event that may be delivered to one and only one observer, and mandates that this observer must be supplied at construction time.

You might slightly regret it if you ever come across the need to instantiate an object without an observer, but the damage is very small, because you can either:

  • Pass null and make your object check for null before invoking the observer, or

  • Simply pass an observer which does nothing; lambdas make this very easy.

You might regret it even a bit more if you come across the need to write a class which offers not one but 5 events, in which case passing 5 observers as constructor parameters might start to look ugly.

That having been said, why do you say that you "don't want to go to the effort of creating an EventHandler and EventArgs derived pair of classes"? Events are not limited to EventHandlers and EventArgs, you can declare an event that operates on a Action<HtmlRegex> so that you may have zero or multiple observers instead of just one observer.

Mike Nakis
  • 32,803
0

In an OOP interpretation, the Action<HtmlRegex> onSelection is an example of the Strategy Pattern. You've parametrized your class over an onSelection operation which is dispatched dynamically – any supplied action will work as long as it conforms to the Action<HtmlRegex> interface. While the Action is a built-in type, you could have also defined your own interface to the same effect, though this would have been more cumbersome for users:

interface SelectionHandler
{
  void Handle(HtmlRegex r);
}

...

class HtmlRegexListViewModel
{
  ...
  public HtmlRegexListViewModel(SelectionHandler onSelection, IDatabase database)
  {
    ...
  }
}

Injecting a strategy dependency through the constructor is the usual way to do it, but any dependency injection technique is possible.

In a functional programming interpretation, this is just a callback. There's nothing special here, and it would be far too common to have a particular name.

amon
  • 135,795