nisse12
nisse12

Reputation: 125

Storing functions in C# class properties?

I need to create a list containing Device objects. These objects have properties that describe their Name, Unit and Conversion. Conversion is a bit special as this is supposed to be a function. For example: If I had a temperature sensor measuing in farenheit, its conversion method should calculate and return the value in celsius. And if I have a moisture sensor, its conversion will be different etc. Below is an example of how I have tried doing this but it doesn't work. I get errors saying that only assignment is allowed on Conversion.

private class Device
{
    public string Name { get; set; }
    public Action Conversion { get; set; }
    public string Unit { get; set; }
}

public static object ParseDecentLab(byte[] bytes)
{

    List<Device> deviceList = new List<Device>()
    {
        new Device()
        {
            Name = "battery-voltage",
            Conversion = (x) => x / 1000,
            Unit = "V"
        },
        new Device()
        {
            Name = "air-temperature",
            Conversion = (x) => (175 * x / 65535) - 45,
            Unit = "°C"
        }
    };

Upvotes: 2

Views: 6415

Answers (2)

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186678

You want Func<double, double> instead of Action; given double (e.g. 4.5 Volt) return double.

x => x / 1000

On the contrary, Action takes no arguments and returns nothing: () => {...}

Code:

// Let's implement immutable class (assigned once, never change)
// in order to prevent occasional errors like device.Name = ... 
private class Device {
  public string Name { get; }
  public Func<double, double> Conversion { get; }
  public string Unit { get; }

  // Let's validate the input (at least, for null)
  public Device(string name, Func<double, double> conversion, string unit) {
    if (null == name)
      throw new ArgumentNullException(nameof(name));
    else if (null == conversion)
      throw new ArgumentNullException(nameof(conversion));
    else if (null == unit)
      throw new ArgumentNullException(nameof(unit));

    Name = name;
    Conversion = conversion;
    Unit = unit;
  }
}

...

List<Device> deviceList = new List<Device>() {
  new Device("battery-voltage", x => x / 1000, "V"),
  new Device("air-temperature", x => (175 * x / 65535) - 45, "°C"),
};

Possible usage:

// What device should we use if we want °C unit?
Device temperature = deviceList
  .FirstOrDefault(item => item.Unit == "°C");

byte[] dataToConvert = new byte[] {123, 45, 79};

// Device found
if (temperature != null) {
  // Converting: for each value in dataToConvert we obtain corresponding t value
  foreach (var value in dataToConvert) {
    double t = temperature.Conversion(value); 
    ...
  }
}

Or you can even have an array of converted values (double[]) with a help of Linq:

byte[] dataToConvert = new byte[] {123, 45, 79};

// Let's throw exception if device has not been found
Device temperature = deviceList
  .First(item => item.Unit == "°C");

double[] results = dataToConvert
  .Select(v => temperature.Convert(v))
  .ToArray();

Upvotes: 0

Rehan Shah
Rehan Shah

Reputation: 1627

Try this code: use Func instead of Action.

Func can return value where as Action Can't.

private class Device
{
   public string Name { get; set; }
   public Func<double,double> Conversion { get; set; }
   public string Unit { get; set; }
}

public static object ParseDecentLab(byte[] bytes)
{        
   List<Device> deviceList = new List<Device>()
   {
      new Device()
      {
         Name = "battery-voltage",
         Conversion = (x) => x / 1000,
         Unit = "V"
      },
      new Device()
      {
         Name = "air-temperature",
         Conversion = (x) => (175 * x / 65535) - 45,
         Unit = "°C"
      }
   };
}

Upvotes: 1

Related Questions