【问题标题】:Taking advantage of PLINQ with custom Enumerable Extensions利用带有自定义可枚举扩展的 PLINQ
【发布时间】:2012-12-19 01:56:41
【问题描述】:

许多自定义 Enumerable 扩展可以根据其他内置操作来实现 - 例如这个简单的便捷方法:

public static bool AnyOf<TElement>(this TElement item, IEnumerable<TElement> items)
{
    return items.Any(a => EqualityComparer<TElement>.Default.Equals(a, item));
}

现在这将强制任何 PLINQ 查询返回到顺序操作,即使 PLINQ 也有 Any - 并且仅与签名更改等效:

public static bool AnyOf<T>(this T item, ParallelQuery<T> items)
{
    return items.Any(a => EqualityComparer<T>.Default.Equals(a, item));
}

但是像这样复制它对我来说似乎很乱。

起初我认为像下面这样的东西可能会起作用,但当然它不起作用^因为扩展方法是静态方法,因此调用Enumerable.Any而不是ParallelQuery.Any的决定是在编译时根据签名做出的.

public static bool AnyOf<TElement, TEnumerable>(this TElement item, TEnumerable items)
    where TEnumerable : class, IEnumerable<TElement>
{
    return items.Any(a => EqualityComparer<TElement>.Default.Equals(a, item));
}

我得出的结论是,如果不创建具有不同签名的每个方法的副本,这是不可能的,但也许我错过了一些东西。 (老是提出不可能的问题!)


也许可以从并行化中受益的帮助器的一个更好的例子(显然可以被链接,等等)是这样的。

public static IEnumerable<string> ToStrings(this IEnumerable<object> ienum)
{
    return ienum.Select(a=> a.ToString());
}

^ 编译错误:

 The type 'ParallelQuery<TElement>' cannot be used as type parameter
 'TEnumerable' in the generic type or method
 'AnyOf<TElement,TEnumerable>(TElement, TEnumerable)'. There is no
 implicit reference conversion from 'ParallelQuery<TElement>' to
 'IEnumerable<TElement>'

另外值得考虑的是,并非所有的 ParallelQuery/Enumerable 方法都是等效的,即使它们可以编译。

【问题讨论】:

  • 真正的问题是什么?
  • 嗯,错了。有点得意忘形:“有没有办法编写一个也适用于 PLINQ 的 LINQ 扩展?”。
  • 你所有的扩展方法都这么短吗?你有几个?

标签: c# linq plinq


【解决方案1】:

我在编写 IQueryable/IEnumerable 扩展时也做过类似的事情。试图分解出涉及声明包含表达式的静态变量的公共位,然后从函数的两个不同版本中引用该表达式。我没有代码了,当我完成时,它非常难看,我对此并不满意。这是一个简单的例子。

Expression<Func<PersonHistory, bool>> IsCurrent = (p) => p.Ends > DateTime.Now && p.Starts <= DateTime.Now;

//Then in each Extension method:
var query = db.PersonHistories.Where(IsCurrent);

最终去重的水平一点都不好,而且会因为泛型参数而变得更加复杂。也许这会给你一个想法。

期待看到其他人的想法。

【讨论】:

    【解决方案2】:

    您可以通过在方法中使用检查转换(即运行时切换)来做到这一点,如下所示:

    public static bool AnyOf<TElement>(this TElement item, IEnumerable<TElement> items)
    {
        var parallelItems = items as ParallelQuery<TElement>
        if(parallelItems != null)
        {
             return parallelItems.Any(a => EqualityComparer<TElement>.Default.Equals(a, item))
        }
        //other runtime checks
        ....
        //else return default IEnumerable implementation
        return items.Any(a => EqualityComparer<TElement>.Default.Equals(a, item));
    }
    

    【讨论】:

    • 这有什么帮助?仍然意味着您有两次相同的代码。
    • @svick ala I've come to the conclusion it's impossible without creating a copy of each method with a different signature, but maybe there's something I've missed & "Is there any way of writing a LINQ extension that also works with PLINQ?" 他的实际问题不是写两次,而是有一个签名,这将取决于枚举行为,这是一个解决方案
    • 但我相信这样做的根本原因是避免代码重复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    相关资源
    最近更新 更多