【问题标题】:Converting the result of Func<T, IConvertible> to int in a lambda expression在 lambda 表达式中将 Func<T, IConvertible> 的结果转换为 int
【发布时间】:2017-12-10 07:16:00
【问题描述】:

我正在抽象一些代码,但遇到了将 Func 的结果转换为整数进行比较的问题。

我有一个函数:

public static IConvertible GetIllnessRateByParity(IEnumerable<ILLNESS_OBJECT> pItems, int pParity, Func<ILLNESS_OBJECT, IConvertible> pSelector)
    {
        var filtered = pItems.Where(a => a.Parity == pParity);
        return Math.Round(filtered.Count(a => Convert.ToInt32(pSelector(a)) == 1) / (double)GetBirthCountByParity(pItems, pParity), 2);
    }

public class ILLNESS_OBJECT
{
    public int ID { get; set; }
    public DateTime Day { get; set; }
    public int Parity { get; set; }
    public int Afterbirth { get; set; }
    public int OvaryInfection { get; set; }
    etc...
}

计算一组数据中特定疾病的发病率。要分析的疾病是通过选择器输入的,该选择器从 ILLNESS_OBJECT 对象中选择一个 IConvertible 值。

我这样调用方法:

GetIllnessRateByParity(data, 1, a => a.AfterBirth)

data 是一个具有非空值的 IEnumerable。

该方法在 Convert.ToInt32(pSelector(a)) 部分引发 InvalidCastException ,但没有明确说明此转换失败的原因,有人可以向我解释一下吗?我的印象是 pSelector(a) 返回一个与 int 兼容的 IConvertible 值,对吧?任何帮助将不胜感激!

编辑:异常消息: CONFIDENTIAL.CLASS.dll 中出现“System.InvalidCastException”类型的异常,但未在用户代码中处理

消息:指定的转换无效。

【问题讨论】:

  • 异常中没有任何额外信息?例如,出现异常的消息是什么?
  • 你检查过你的pSelector返回的IConvertible的具体实现了吗?可能是不支持 ToInt32 / 的实现在那里抛出异常?
  • “我的印象是 pSelector(a) 返回一个与 int 兼容的 IConvertible 值” - 我不认为是这种情况,但我也不认为是这样重要的。正在发生的事情是它正在调用ToInt32 的以下重载:msdn.microsoft.com/en-us/library/23511zys(v=vs.110).aspx,然后按照 Jeroen 所说的去做。
  • 为什么不把Func&lt;ILLNESS_OBJECT, IConvertible&gt; pSelector改成Func&lt;ILLNESS_OBJECT, int&gt; pSelector
  • 没有采用 IConvertable 的 Convert.ToInt32 的重载,这就是运行时将回退到调用 @Chris 指出的重载的原因。你也用函数式编程标记了这个问题,但是你使用的接口比函数式更 OOP,Roman 的想法更实用。而且你的班级有可变字段。

标签: c# linq generics casting functional-programming


【解决方案1】:

查看this

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var data = new List<ILLNESS_OBJECT>{
                new ILLNESS_OBJECT{ ID = 1, Parity = 1, Day = DateTime.Now, SomeString = "1", Long = 2L},
                new ILLNESS_OBJECT{ ID = 2, Parity = 2, Day = DateTime.Now, SomeString = "1", Long = 2L},
                new ILLNESS_OBJECT{ ID = 3, Parity = 1, Day = DateTime.Now, SomeString = "2", Long = 1L},
                new ILLNESS_OBJECT{ ID = 4, Parity = 1, Day = DateTime.Now, SomeString = "3", Long = 3L},
        };
        Console.WriteLine(GetIllnessRateByParity(data, 1, x => x.ID));
        Console.WriteLine(GetIllnessRateByParity(data, 1, x => int.Parse(x.SomeString)));
        Console.WriteLine(GetIllnessRateByParity(data, 1, x => (DateTime.Now - x.Day).Days + 1));
        Console.WriteLine(GetIllnessRateByParity(data, 1, x => (int)x.Long));
    }

    public static IConvertible GetIllnessRateByParity(IEnumerable<ILLNESS_OBJECT> pItems, int pParity, Func<ILLNESS_OBJECT, int> pSelector)
    {
        var filtered = pItems.Where(a => a.Parity == pParity);
        return Math.Round(filtered.Count(a => pSelector(a) == 1)/(double)1, 2);
    }

}

public class ILLNESS_OBJECT
{
    public int ID { get; set; }
    public DateTime Day { get; set; }
    public int Parity { get; set; }
    public int Afterbirth { get; set; }
    public string SomeString { get; set; }
    public long Long { get; set; }
}

【讨论】:

    【解决方案2】:

    最后一个参数叫做pSelector,但它实际上并不是一个选择器。

    它只是返回一个值(int、float...),而实际的选择是由labmda在Count方法中完成的:

    filtered.Count(a => Convert.ToInt32(pSelector(a)) == 1)
    

    所以 pSelector 将返回某种数据类型的值,然后您需要将其转换为 int32。

    但您只需要将其与 1 进行比较。鉴于您问题中的信息,您实际上并不需要 int32,您需要一个 bool。

    那么,如果你将 pSelector 重构为一个选择器,通过在选择器内将比较移动到 1 来实现:

    public static double GetIllnessRateByParity(IEnumerable<ILLNESS_OBJECT> pItems, int pParity, Func<ILLNESS_OBJECT, bool> pSelector)
    {
        var filtered = pItems.Where(a => a.Parity == pParity);
        return Math.Round(filtered.Count(pSelector) / (double)GetBirthCountByParity(pItems, pParity), 2);
    }
    GetIllnessRateByParity(data, 1, a => a.AfterBirth==1)
    

    【讨论】:

    • 我认为比较是一个实现细节,所以我将它移到方法中,不过我同意您对我的命名方案的反馈,谢谢!
    • 经过考虑我将接受您的回答,这种方式不需要困难的转换,并且使该方法比选择器更具灵活性
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多