【问题标题】:Compile Error 'cannot convert' on Declared Delegate声明的委托上的编译错误“无法转换”
【发布时间】:2020-07-04 16:31:55
【问题描述】:

在 LINQ 中将委托作为 Where 参数传递时遇到问题。

我在命名空间中声明一个委托:

public delegate bool FilterInt<in T>(T x);

我已经做了一个分配给委托的方法,然后分配它:

    public static bool FilterData(int val)
    {
        if (val < 10)
            return true;
        return false;

    }

当我尝试使用委托过滤列表时,出现编译时错误“无法从 FilterInt 转换为 Func:

listInt.Where(_filterer);

但是,我可以毫无问题地使用以下(隐含的委托?):

listInt.Where(FilterData);

如果我按照以下方式在命名空间中定义我自己的委托,则当我关注委托 here 上的 MSDN 文档时,我遇到了与 _comparer 相同的问题:

public delegate int Comparison<in T>(T x, T y);

public static int CompareLength(string left, string right) =>
     right.Length.CompareTo(left.Length);

readonly Comparison<string> _comparer = CompareLength;

list.Sort(_comparer);

但是,如果我省略比较的初始声明,它可以正常工作(注意 - 比较存在于系统命名空间中)。

我认为这是我最初的委托声明的问题。

【问题讨论】:

  • _filterer的类型是什么?
  • 你可以这样使用 .Where(x => FilterInt(x))
  • _filterer 的类型为 FilterInt。我知道有办法解决这个问题,但我想弄清楚为什么这种方法特别不起作用

标签: c# .net


【解决方案1】:

Enumerable.Where 接受 Func&lt;TSource, bool&gt; 委托。如果您在 Func&lt;T,TResult&gt; 上查找文档,您会发现它被声明为:

public delegate TResult Func<in T,out TResult>(T arg);

因此,您需要将您的委托“转换”为Func&lt;TSource, bool&gt; 的实例,这可以通过直接创建委托的实例来实现:

new[] { 1 }.Where(new Func<int, bool>(_filterer));

或使用匿名 (lambda) 函数:

new[] { 1 }.Where(i => _filterer(i));

这实际上是一个语法糖transformed by compiler 变成这样的东西:

 // generated class to hold your lambda:
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
    public FilterInt<int> _filterer;
    // your lambda: 
    internal bool <M>b__0(int i)
    {
        return _filterer(i);
    }
}

   
// instantiation of new Func<int, bool>
Enumerable.Where(array, new Func<int, bool>(<>c__DisplayClass0_.<M>b__0));

方法组调用 (listInt.Where(FilterData);) 也是语法糖 expanded by compiler 用于创建新委托:

Enumerable.Where(array, new Func<int, bool>(FilterData));

【讨论】:

  • 这是有道理的!我仍然不明白为什么我能做的是为什么使用比较 _comparer = CompareLength; 的版本非常相似和 list.Sort(_comparer);有用吗?除了 Comparison 是已经在 .NET 中声明的类型之外,它完全一样。再次不太有意义的是,如果我使用与最初声明的代码完全相同的代码覆盖命名空间中的 Comparison 委托的声明,则 list.Sort(_comparer) 会抛出与我原来的问题相同的错误
  • @TomBennett 没有完全关注你。 List.Sort 具有接受 Comparison&lt;T&gt; 的重载,那么为什么它不能工作呢?
  • 这正是我没有掌握的。我已经用这方面的更多信息更新了原始帖子。本质上,如果我声明一个名为 Comparison 的公共委托并将其与系统名称空间声明完全匹配,我的委托字段将不起作用。如果我删除它,而是委托字段查看系统命名空间声明,它确实有效。它让我相信我在原始帖子中所做的事情应该确实有效,并且在当前命名空间中声明委托是一个问题(尽管我知道这没有任何意义)。
  • @TomBennett 您已经定义了自己的委托。与内建同名无关紧要,它们是不同的,Sort 期待内建。
  • @TomBennett 如果你定义了一些与内置同名的类,你将无法将它的实例传递给期望内置实例的方法。与代表是一样的,实际上在IL they are classes too内部。
【解决方案2】:

你可以这样打电话。

Where(new Func<int, bool>(FilterInt))

或者

.Where(x => FilterInt(x))

【讨论】:

  • 太棒了,这些工作。你有什么理由可以告诉我为什么我的方法不起作用吗?它更多的是用于教育目的而不是实际应用。
【解决方案3】:

发生错误是因为 where 子句需要一个 Func&lt;TSource, TResult&gt; 和一个布尔值作为 TResult 而不仅仅是一个 Func&lt;int&gt; 作为其代表。

IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

按照您的意愿使用它的最接近的方法是这样的,但我不认为这是一个好的实现。

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

public delegate Func<T1, T2> FilterInt<in T1, out T2>(T1 value);

namespace DelegateTest
{
    class Program
    {
        private static readonly List<int> List = new List<int> {0, 1, 2, 10, 11, 12};

        static void Main(string[] args)
        {
            FilterInt<int, bool> filterInt = FilterData;
            var result = List.Where(x => filterInt.Invoke(x).Invoke(x));
            foreach (var item in result) Console.WriteLine(item.ToString());
        }

        private static Func<int, bool> FilterData(int value) => _ => value < 10;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多