Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Closure-based State: C#

DZone's Guide to

Closure-based State: C#

Ted Neward breaks down Closure-based State implemented in C#. Read on for examples and further analysis.

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

A Closure-based State implementation in C#.

Implementation: C#

While normally we don’t think of C# as a functional language. Its support for closures (necessary in order to implement LINQ) means that C# can make use of Closure-based State with very little in the way of extra-linguistic magic. However, the syntax isn’t always pretty, and it can get downright ugly in a number of scenarios.

First, let’s start with a basic example of holding closure-based state using just a function; no classes, nothing tricky (yet):

public class App
{
  public static void Main()
  {
    Func<int, int> productOperation = ((Func<Func<int,int>>)( () => {
      var state = 100;
      Func<int, int> result = delegate(int adjust)
      {
        state += adjust;
        return state;
      };
      return result;
    }))();

    Console.WriteLine("Result = {0}", productOperation(100));
  }
}

Okay, so I lied, and things got tricky fast; that’s because C# is a strongly-typed language, for starters, and doesn’t like the idea of a function literal being assigned to "var" (it very explicitly says you can’t do that), so we need to manually type the various instances in use. The inner Func/delegate is the actual method that we want to invoke, and the outer one (which is immediately invoked after definition) is simply there to hold the closure-based state.

Objects

Should the closure-based state want to be part of an object, C# makes it trickier; the interface for the object definition must appear in a sufficiently wide enough scope that clients can see it, yet the actual implementation has to be deferred until it can be defined inside the closure. In many respects, the easiest way to do this (since C# does not support the concept of types defined inline in a method, a la anonymous inner classes in Java) is to make this a Dynamic Object:

public class Product
{
  private Func<int> stateGetter;
  private Action<int> stateSetter;
  private Func<int,int> operation;

  private Product(Func<int> sg, Action<int> ss, Func<int,int> op) {
    stateGetter = sg;
    stateSetter = ss;
    operation = op;
  }

  public int State
  {
    get { return stateGetter(); }
    set { stateSetter(value); }
  }

  public int Operation(int adjust) { return operation(adjust); }

  public static Product New()
  {
    var state = 100;

    Func<int> getter = () => { return state; };
    Action<int> setter = (int v) => { state = v; };
    Func<int,int> operation = (int adj) => { state += adj; return state; }; 

    return new Product(getter, setter, operation);
  }
}

public class App
{
  public static void Main()
  {
    Product p = Product.New();
    Console.WriteLine("Result = {0}", p.Operation(100));
  }
}

The "State" property here is simply to demonstrate how a property could also access the closure-based state; it would actually be defeating much of the purpose of Closure-based State if the state were directly accessible (and modifiable) via the property (in most cases).

Note that the use of Closure-based State will almost always require some form of Constructor Function to create the object, since a closure by definition is function/method-local variable state "closed over" by a function literal. In the above, the Constructor Function is the static "New" method.

Some may prefer to avoid the trampoline through the constructor for the Product, by the way:

public class Product
{
  private Func<int> stateGetter;
  private Action<int> stateSetter;
  private Func<int,int> operation;

  public int State
  {
    get { return stateGetter(); }
    set { stateSetter(value); }
  }

  public int Operation(int adjust) { return operation(adjust); }

  public static Product New()
  {
    var state = 100;

    var result = new Product();
    result.stateGetter = () => { return state; };
    result.stateSetter = (int v) => { state = v; };
    result.operation = (int adj) => { state += adj; return state; }; 
    return result;
  }
}

Fully-dynamic Closure-based State

C# can also make use of fully dynamic objects using dynamic here:

public class DynProduct
{
  public static dynamic New()
  {
    var state = 100;

    dynamic result = new ExpandoObject();
    result.State = ((Func<int>) (() => { return state; }));
    result.Operation = (Func<int,int>) ((int adjust) => { state += adjust; return state; });
    return result;
  }
}

public class App
{
  public static void Main()
  {
    dynamic d = DynProduct.New();
    Console.WriteLine("Result = {0}", d.Operation(100));
  }
}

The casts are necessary; the compiler simply will not accept the code without them.

This approach eliminates the need to define the object interface, since that will be implicitly done via the "dynamic" facilities of the C# language. The drawback, of course, is that the type will no longer have a type-checked interface that the compiler can verify for correctness. This will allow for a certain degree of flexibility in that interface over time, but will also potentially yield errors that could have been caught at compile-time. See the C# Dynamic Object implementation for more details on the pros/cons of dynamic objects in C#.

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:
c# ,closures

Published at DZone with permission of Ted Neward, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}