【问题标题】:Storing functions in C# class properties?在 C# 类属性中存储函数?
【发布时间】:2019-01-31 10:40:29
【问题描述】:

我需要创建一个包含设备对象的列表。这些对象具有描述其名称、单位和转换的属性。转换有点特殊,因为这应该是一个函数。例如:如果我有一个以华氏度为单位测量的温度传感器,它的转换方法应该计算并返回以摄氏度为单位的值。如果我有一个湿度传感器,它的转换会有所不同等。下面是我如何尝试这样做但它不起作用的示例。我收到错误消息,说转换时只允许赋值。

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"
        }
    };

【问题讨论】:

  • 你想要 Func 而不是 Action
  • public Func&lt;double, double&gt; Conversion { get; set; }: 给定double 返回double
  • 旁注,作为设计指南,我会尽量隐藏该属性。代表是奇怪的、不可变的类型。您的用户现在可以为一个 Func 订阅多次转化

标签: c# function delegates


【解决方案1】:

你想要Func&lt;double, double&gt; 而不是Action;给定double(例如4.5 Volt)返回double

x => x / 1000

相反,Action 不接受任何参数并且不返回任何内容:() =&gt; {...}

代码:

// 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"),
};

可能的用法:

// 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); 
    ...
  }
}

或者您甚至可以在 Linq 的帮助下拥有一个转换后的值数组 (double[]):

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();

【讨论】:

  • 我试过这个,它似乎工作。但是,我以后如何调用 Conversion 呢?我得到一个字节数组,我可以用它来计算这些值。然后应将这些值放入转换中。
  • @nisse12:您可能应该遍历数组并转换每个项目。
【解决方案2】:

试试这个代码: 使用Func 而不是Action

Func 可以返回值,而 Action 不能。

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"
      }
   };
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-25
    • 2020-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-06
    • 2020-08-02
    • 2023-03-29
    相关资源
    最近更新 更多