制作不需要的对象;t从IEnumerable<>;可通过LINQ进行查询

本文关键字:可通过 gt lt LINQ 查询 IEnumerable 不需要 对象 | 更新日期: 2023-09-27 18:29:44

我有一个名为IDCollection的类,它实际上是基于List<>Dictionary<,>或类似IEnumerable的类型,而不是。尽管它有一个索引器,但它基于一个名为innerList的私有Dictionary<string, ID>,因此类的对象本身不是列表。它也不通过自己的this访问器持有键值对或项的集合。

是否可以在LINQ表达式中查询IDCollection对象?如果是,如何

例如,假设对象名为testIdColl。那样的话

from KeyValuePair<string, ID> k in testIDColl
select k;

等等。

IQueryable接口是否涉及?

制作不需要的对象;t从IEnumerable<>;可通过LINQ进行查询

这就是您需要对类执行的全部操作:

public class IDCollection : IEnumerable<KeyValuePair<string, ID>>
{
    private IDictionary<string, ID> List = new Dictionary<string, ID>();
    public IEnumerator<KeyValuePair<string, ID>> GetEnumerator()
    {
        return List.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

然后你可以运行这个代码:

var testIDColl = new IDCollection();
var query =
    from KeyValuePair<string, ID> k in testIDColl
    select k;

如果你愿意,你可以让整个IEnumerable<...>接口私有化,比如:

public class IDCollection : IEnumerable<KeyValuePair<string, ID>>
{
    private IDictionary<string, ID> List = new Dictionary<string, ID>();
    IEnumerator<KeyValuePair<string, ID>> IEnumerable<KeyValuePair<string, ID>>.GetEnumerator()
    {
        return List.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return List.GetEnumerator();
    }
}

现在什么都没有直接暴露出来。

我根据Jakub的回答创建了三个实例方法的实现:

public class IDCollection
{
    private IDictionary<string, ID> List = new Dictionary<string, ID>() { { "x", new ID() }, } ;
    public IEnumerable<KeyValuePair<string, ID>> Where(Func<KeyValuePair<string, ID>, bool> selector)
    {
        return List.Where(selector);
    }
    public IEnumerable<TResult> Select<TResult>(Func<KeyValuePair<string, ID>, TResult> selector)
    {
        return List.Select(selector);
    }
}

执行基本查询不需要Cast方法。

我建议添加一个显式的AsEnumerable()方法来公开LINQ运算符的全部范围。

这将是最简单、最健壮的查询方式:

public class IDCollection
{
    private IDictionary<string, ID> List = new Dictionary<string, ID>() { { "x", new ID() }, } ;
    public IEnumerable<KeyValuePair<string, ID>> AsEnumerable()
    {
        return List.Select(x => x);
    }
}

查询必须看起来像:

var query =
    from k in testIDColl.AsEnumerable()
    where k.Key == "x"
    select k;

最简单的解决方案是实现IEnumerable<T>。这只是一个方法,您通常可以将其委托给内部集合的GetEnumerator()或使用yield return轻松实现。

您的类型的名称(IDCollection)建议它应该实现IEnumerable<T>,可能是其他一些集合接口(例如ICollection<T>IReadonlyCollection<T>)。

如果出于某种原因您不想使用IEnumerable<T>,您仍然可以使其工作。首先,您需要了解编译器是如何处理查询表达式的。

LINQ查询表达式首先由编译器转换为一系列方法调用。例如查询表达式

from KeyValuePair<string, int> item in collection
where item.Key == "abc"
select item.Value;

被翻译成

testIDColl
    .Cast<KeyValuePair<string, int>>()
    .Where(item => item.Key == "abc")
    .Select(item => item.Value);

要在查询表达式中使用类型作为源,您所需要做的就是编译上面的代码。这意味着您将需要CastWhereSelect(以及其他方法,如GroupByOrderBy)方法作为实例方法或扩展方法。

例如,以下类和扩展方法将编译上述查询表达式(尽管这些方法根本不起任何作用):

class IDCollection
{
}
static class IDCollectionExtensions
{
    public static IDCollection Cast<TResult>(this IDCollection source)
    {
        return source;
    }
    public static IDCollection Where(this IDCollection source, Func<KeyValuePair<string, int>, bool> selector)
    {
        return source;
    }
    public static IDCollection Select<TResult>(this IDCollection source, Func<KeyValuePair<string, int>, TResult> selector)
    {
        return source;
    }
}

实例方法也会起作用:

class IDCollection
{
    public IDCollection Cast<TResult>()
    {
        return this;
    }
    public IDCollection Where(Func<KeyValuePair<string, int>, bool> selector)
    {
        return this;
    }
    public IDCollection Select<TResult>(Func<KeyValuePair<string, int>, TResult> selector)
    {
        return this;
    }
}