在 LINQ 查询中使用 List<T>.Contains(T) 出错

4

想知道为什么这个不起作用。非常感谢您的帮助。

    static void Main(string[] args)
    {
        List<int> foo = new List<int> { 1, 2, 3 };
        var myResult = MyTest<int>(foo);
    }

    private static List<int> MyTest<T>(List<T> input)
    {
        List<int> bar = new List<int> { 2, 3, 4 };
        return bar.Where(b => input.Contains(b)).ToList();
    }

MyTest()函数的预期输出是一个列表{2, 3}。然而,在处,编译器报告了两个错误,分别是:
  1. 参数1:无法将'int'转换为'T'

  2. System.Collections.Generic.List.Contains(T)的最佳重载方法具有一些无效的参数

如果不使用泛型列表,则Where()子句可以正常工作。
这只是我的实际问题的简化,所以请不要卡在“为什么要写这个?”上。问题是错误及其发生原因。
namespace SandBox
{

class Foo
{
    public int FooInt { get; set; }
    public string FooString { get; set; }
}

class Program
{
    private static List<Foo> fooList = new List<Foo> {
            new Foo() {FooInt = 1, FooString = "A"},
            new Foo() {FooInt = 2, FooString = "B"},
            new Foo() {FooInt = 3, FooString = "C"}
    };

    static void Main(string[] args)
    {
        List<int> myIntList = new List<int> { 1, 2 };
        var myFirstResult = GetFoos<int>(myIntList);

        List<string> myStringList = new List<string> { "A", "B" };
        var mySecondResult = GetFoos<string>(myStringList);
    }

    /// <summary>
    /// Return a list of Foo objects that match the input parameter list
    /// </summary>
    private static List<Foo> GetFoos<T>(List<T> input)
    {
        //***
        // Imagine lots of code here that I don't want to duplicate in 
        // an overload of GetFoos()
        //***

        if (input is List<int>)
        {
            //Use this statement if a list of integer values was passed in
            return fooList.Where(f => input.Contains(f.FooInt));
        }
        else if (input is List<string>)
        {
            //Use this statement if a list of string values was passed in
            return fooList.Where(f => input.Contains(f.FooString));
        }
        else
            return null;
    }
}
}

相同的编译器错误会在 input.Contains(f.Property) 上报告。

3
唯一可行的方法是如果 inputList<int>,为什么你要使用 <T> - Austin Salonen
5
注:return bar.Intersect(input); 会更高效。 - Tim Schmelter
1
由于T通常不是int类型,在编译时函数内部不能假设T为int类型。 - Tengiz
2
正如奥斯汀所指出的那样,它只适用于int。考虑一下如果有可能编译并且有人调用了MyTest<string>(someStrings) - Sergey Berezovskiy
你能否发布一个更详细的问题版本呢?因为当前的提问方式并不具备明确的意义。 - user287107
返回 input.OfType<int>().Intersect(bar); 如果这不能解决问题,请告诉我们 MyTest<NotAnInt>() 的期望输出是什么。 - Tormod
5个回答

2
另外还有一个解决方案。
static void MainT(string[] args)
{
    List<int> foo = new List<int> { 1, 2, 3 };
    var myResult = MyTest<int>(foo);
}

private static List<int> MyTest<T>(List<T> input) where T : IEquatable<int>
{
    List<int> bar = new List<int> { 2, 3, 4 };
    return bar.Where(b => input.Any(i => i.Equals(b))).ToList();
}

这是一个很棒的答案。从来没有想过在签名中使用IEquatable<int>。内部的LINQ查询(i => ...)也非常流畅易读。 - Reacher Gilt

2

input 应该是一个 List<int>

每当你调用这个函数时,如果 T 不是 int,那么你就会知道它总是返回一个空列表。

T 不是 int 时,这个函数没有太多意义。


1

仅看这个函数本身。

private static List<int> MyTest<T>(List<T> input)
{
    List<int> bar = new List<int> { 2, 3, 4 };
    return bar.Where(b => input.Contains(b)).ToList();
}

如果T是对象或字符串,那么没有任何东西可以阻止T成为这些类型之一。如果T是其中之一,语句将毫无意义。
编译器发出警告,因为您允许在方法体中与语句不相符的类型。

0

试试这个:

static void Main(string[] args)
{
    List<int> foo = new List<int> { 1, 2, 3 };
    var myResult = MyTest<int>(foo);
}

private static List<int> MyTest<T>(List<T> input)
{
    List<int> bar = new List<int> { 2, 3, 4 };
    return bar.Where(b => input.OfType<int>().Contains(b)).ToList();
}

问题在于编译器不知道T是什么类型。由于T可以是任何类型,因此您无法调用期望int的方法(input.Contains)。

1
MyTest<T>(List<T> input) 更改为 MyTest<int>(List<int> input) 显然更好! - Trevor Pilley
如果您知道输入始终是List<int>,那么可以确定。如果您不知道(我假设这就是首先使其成为通用类型的原因),那么最好考虑不同类型的输入。 - Ian Newson

0
编译器无法保证任何 <T> -- 任何 <T>,DateTime,对象或其他类型 -- 都可以转换为 int。这就是你遇到第一个错误的原因。
在某些情况下,您可能能够在函数签名中指定对象的类型:
 private static List<int> MyTest<T>(List<T> input)  where T : someObject

这对你的情况不起作用,因为int是一个结构体。你可以用很多其他方法来处理它(其他答案提供了一些很棒的方法),但你必须以某种方式调整你当前的策略。


1
你不能按照你的方式将T作为对象。Object类是密封的。 - nawfal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接