【问题标题】:How to change the generic type of a lambda function in C#如何在 C# 中更改 lambda 函数的泛型类型
【发布时间】:2020-08-23 19:41:05
【问题描述】:

我在Predicate<T> 中转换内部类型时遇到以下问题。我有一个实现基本接口的类。除了接口之外,该类还有一些高级属性和方法。

public interface IBasic { /* ... */ }
public class Advanced : IBasic { /* ... */ }

在一个方法中,我在 Advanced 类上获得了许多谓词,我想将它们分类到两个列表中:1) 仅使用 IBasic 接口的谓词 2) 需要来自 @ 的所有功能的谓词987654328@班级。

List<Predicate<IBasic>> basic = new List<Predicate<IBasic>>();
List<Predicate<Advanced>> advanced = new List<Predicate<Advanced>>();

public void SetPredicates(params Predicate<Advanced>[] predicates)
{
    foreach(var item in predicates)
    {
        //Of course the `is` keyword does not work here, always returning false.
        //Is it possible to do the check on the lambda function in item in a different way?
        if (item is Predicate<IBasic>)
            basic.Add((Predicate<IBasic>)item); //And this cast throws an exception of course.
        else
            advanced.Add(item);
    }
}

问题

  • 有可能吗?
  • 如何检查谓词的泛型类型是否可以简化为IBasic 接口?
  • 如何执行谓词转换?

谓词的数量很少,所以我可以使用较慢的东西,例如 dynamic 类型的反射。

背景

谓词分区的原因是我有很多Advanced 实例。我想首先通过只需要 IBasic 接口的谓词对它们进行预过滤,因为它们的评估速度非常快。然后我将不得不在第二遍中使用Advanced 的复杂方法(不在IBasic 中)过滤更少数量的实例,因为它们需要很长时间来评估。

【问题讨论】:

  • 这似乎完全破坏了Liskov Substitution Principle。在我看来,设计出了点问题,现在你有一个XY problem
  • @CamiloTerevinto 谢谢你的建议。我不知道它会如何破坏它。换个角度看,如果你找到更好的设计,请告诉我:我有类 Advanced 具有各种属性。有些属性评估很快,但有些需要很长时间。我有很多 Advanced 实例要检查,每个实例都需要遵守一些谓词。我希望首先使用快速谓词对它们进行预过滤。为了确定哪些谓词是 fast 的,我将 fast 属性提取到IBasic 接口,现在我正在尝试进行类型检查和强制转换。
  • @PavelAnikhouski 谢谢!是的,我正在尝试在运行时进行逆变泛型转换。根据我从文章中的理解,如果我在 lambda 主体上进行强制转换,它可以工作,例如SetPredicates((IBasic b) =&gt; b.Count &gt; 1 ) 但我无法控制如何调用该方法。我将尝试将其分为两种方法,看看是否有帮助,尽管设计更差。

标签: c# foreach casting type-conversion


【解决方案1】:

您可以在谓词类中实现一个重载的构造函数。一个用于基本,另一个用于高级。还要对项目进行布尔检查,看看它是否是基本检查?

也许您也可以对项目的某些属性进行某种布尔检查,看看它是否是基本的

List<Predicate<IBasic>> basic = new List<Predicate<IBasic>>();
List<Predicate<Advanced>> advaned = new List<Predicate<Advanced>>();

public void SetPredicates(params Predicate<Advanced>[] predicates)
{
    foreach(var item in predicates)
    {
        //Of course the `is` keyword does not work here, always returning false.
        //Is it possible to do the check on the lambda function in item in a different way?
        if (item is Predicate<IBasic>)
            basic.Add(new Predicate(item, isBasic)); //And this cast throws an exception of course.
        else
            advanced.Add(new Predicate(item));
    }
}

【讨论】:

  • 我正在使用来自 System 程序集的 Predicate 类。它是密封的,没有机会重载构造函数:docs.microsoft.com/en-us/dotnet/api/…
  • 你可以试试这个public void SetPredicates(params Predicate&lt;Advanced&gt;[] predicates) { foreach(var item in predicates.FindAll( x =&gt; x is IBasic)) { basic.Add(item); } }
  • 感谢您的努力,但x =&gt; x is IBasic 是错误的,应该是x =&gt; x is Predicate&lt;IBasic&gt;,这与我在答案中的示例相同。
【解决方案2】:

@PavelAnikhouski 为我指明了正确的方向。问题是逆变would be impractical for arrays。不过,可以在编译时执行逆变转换通过省略params

所以不要调用:

item.SetPredicates(x => !x.Empty /*IBasic*/, x => x.IsGlobalOptimum /*Advanced*/);

它可以分解成单独的调用,每个调用都被编译器正确识别:

item.AddPredicate(x => !x.Empty); //IBasic
item.AddPredicate(x => x.IsGlobalOptimum); //Advanced

Advanced 类有两个重载方法:

public void AddPredicate(Predicate<IBasic> p)
{
    basic.Add(p);
}

public void AddPredicate(Predicate<Advanced> p)
{
    advanced.Add(p);
}

这不是最好的界面,能够一次批量添加多个谓词会更好,但它确实有效。如果有人找到更优雅的解决方案,我将很高兴看到并讨论它。

【讨论】:

    猜你喜欢
    • 2023-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-14
    相关资源
    最近更新 更多