C# 将LINQ与包装阵列一起使用

C# 将LINQ与包装阵列一起使用,c#,arrays,linq,C#,Arrays,Linq,我是LINQ的新手,我正在做一个简单的项目来学习这项技术的特性 目前我有一个静态类,它封装了一个对象数组(一种简单的工厂)。假设它看起来如下所示: public static class Factory { private static Item[] items = new Item[] { // items are created here }; // ... } 现在我可以在工厂中添加一些函数来查询内部数组,例如 public st

我是LINQ的新手,我正在做一个简单的项目来学习这项技术的特性

目前我有一个静态类,它封装了一个对象数组(一种简单的工厂)。假设它看起来如下所示:

public static class Factory
{
    private static Item[] items = new Item[]
    {
        // items are created here
    };

    // ...
}
现在我可以在工厂中添加一些函数来查询内部数组,例如

    public static Item GetByID(ItemID id)
    {
        var query = 
            from item in items
            where item.ID == id
            select item;

        return query.First();
    }
但是,这需要我修改Factory类的内部结构。有没有办法从“外部世界”编写这样的查询

public class OtherClass
{
    var result = from it in Factory select ...
}

是的,你可以。只需在“外部世界”的工厂使用linq即可:

public class OtherClass
{
    public Item Get(ItemId id)
    {
       return Factory.Items.SingleOrDefault(i => i.ID == id);
    }
}

当然,要做到这一点,您需要将
数组的访问修饰符更改为
公共

是的,您可以。只需在“外部世界”的工厂使用linq即可:

public class OtherClass
{
    public Item Get(ItemId id)
    {
       return Factory.Items.SingleOrDefault(i => i.ID == id);
    }
}
当然,要做到这一点,您需要将
数组的访问修饰符更改为
公共

使用表达式树

public class OtherClass
{ 
    public Item Get(ItemId id)
    {
         return Factory.Get(i => i.id == id);
    }
}
并将get方法更改为

 public Item Get(Expression<Func<Item,bool>> filter)
 {
      return items.SingleOrDefault(filter);
 }
公共项获取(表达式筛选器)
{
返回项。SingleOrDefault(过滤器);
}
但是,除非在factory类中封装一些其他逻辑,即仅选择未软删除的行,否则这种方法毫无意义。

使用表达式树

public class OtherClass
{ 
    public Item Get(ItemId id)
    {
         return Factory.Get(i => i.id == id);
    }
}
并将get方法更改为

 public Item Get(Expression<Func<Item,bool>> filter)
 {
      return items.SingleOrDefault(filter);
 }
公共项获取(表达式筛选器)
{
返回项。SingleOrDefault(过滤器);
}

但是,除非您在factory类中封装一些其他逻辑,即仅选择未软删除的行,否则这种方法没有什么意义。

有很多选项

最简单的方法是公开一个公共属性,该属性允许您想要允许的内容:

public static class Factory
{
    private static Item[] items = new Item[]
    {
        // items are created here
    };

    public static IEnumerable<IReadableItem> Items{ get { return items; } }
    // ...
}

如果您真的想变得更花哨,您甚至可以实现自己的LINQ提供程序。从语言的角度来看,LINQ表达式语法只映射到特定的方法名,因此如果您有一个实现了
.Where()
.Select()
方法的类,那么您可以按照自己的意愿实现它,人们在尝试做你的方法不支持的事情之前不会知道有什么不同。

有很多选择

最简单的方法是公开一个公共属性,该属性允许您想要允许的内容:

public static class Factory
{
    private static Item[] items = new Item[]
    {
        // items are created here
    };

    public static IEnumerable<IReadableItem> Items{ get { return items; } }
    // ...
}

如果您真的想变得更花哨,您甚至可以实现自己的LINQ提供程序。从语言的角度来看,LINQ表达式语法只映射到特定的方法名,因此如果您有一个实现了
.Where()
.Select()
方法的类,那么您可以按照自己的意愿实现它,人们在尝试做一些您的方法不支持的事情之前不会知道有什么不同。

一种可能性是为非静态类实现
IQueryable

public class Factory<T> : IQueryable<T>
{
    protected T[] _items = new T[]{};

    public Type ElementType
    {
        // or typeof(T)
        get { return _items.AsQueryable().ElementType; }
    }

    public System.Linq.Expressions.Expression Expression
    {
        get { return _items.AsQueryable().Expression; }
    }

    public IQueryProvider Provider
    {
        get { return _items.AsQueryable().Provider; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ( IEnumerator<T> )_items.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _items.GetEnumerator();
    }
}
用法:

var specificItem = Factories.ItemFactory
    .Where( item => item.ID == id )
    .SingleOrDefault();

一种可能性是为非静态类实现
IQueryable

public class Factory<T> : IQueryable<T>
{
    protected T[] _items = new T[]{};

    public Type ElementType
    {
        // or typeof(T)
        get { return _items.AsQueryable().ElementType; }
    }

    public System.Linq.Expressions.Expression Expression
    {
        get { return _items.AsQueryable().Expression; }
    }

    public IQueryProvider Provider
    {
        get { return _items.AsQueryable().Provider; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ( IEnumerator<T> )_items.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _items.GetEnumerator();
    }
}
用法:

var specificItem = Factories.ItemFactory
    .Where( item => item.ID == id )
    .SingleOrDefault();


如果它是公共财产,您可以访问它
Factory.WhicheverCollection.Where(item=>item.id==whicheverid)
有什么问题吗?我的意思是使用内部数组而不公开它。类似于添加[]运算符,允许用户将对象用作容器。如果可以对类进行迭代,则可以这样做。这样做的方法是使类实现
IEnumerable
。从内存来看,Linq做了一些奇怪的事情,可以使用GetIterator或其他工具处理任何事情,但您可能不应该相信这一点。明白了-您认为哪种方法更好?将内部集合公开的方法,或者为您的财产实现IEnumerable?一个公共的
get
和一个私有的
set
的方法,我肯定会这样做。接口在相当大的程度上声明了意图。如果你想让人们把
工厂
看作一个列表,那么
IEnumerable
是一个不错的选择。在我看来,任何名为
Factory
的东西都不应该是列表,因为没有人会想到这一点。如果它是公共财产,你可以访问它
Factory.WhicheverCollection.Where(item=>item.id==whicheverid)
有什么问题吗?我的意思是使用内部数组而不公开它。类似于添加[]运算符,允许用户将对象用作容器。如果可以对类进行迭代,则可以这样做。这样做的方法是使类实现
IEnumerable
。从内存来看,Linq做了一些奇怪的事情,可以使用GetIterator或其他工具处理任何事情,但您可能不应该相信这一点。明白了-您认为哪种方法更好?将内部集合公开的方法,或者为您的财产实现IEnumerable?一个公共的
get
和一个私有的
set
的方法,我肯定会这样做。接口在相当大的程度上声明了意图。如果你想让人们把
工厂
看作一个列表,那么
IEnumerable
是一个不错的选择。在我看来,任何名为
Factory
的东西都不应该是列表,因为没有人会想到这一点。这就是问题所在-这可以在不公开内部集合的情况下完成,而是使用包装类吗?您必须向工厂添加一个Get方法,该方法是公共的,它返回数组
,“外部世界”可以使用linq对其进行操作。认识到这只是语义上的不同,但却完成了相同的事情;他可以让类实现
IEnumerable
。这对于OP试图完成的任务来说是不必要的复杂。我同意。只有在你说他“必须”按你的方式做的时候,我才不同意。我会按你的方式来做。这就是问题所在-这可以在不公开内部集合的情况下完成,而是使用包装类吗?你必须向工厂添加一个Get方法,该方法是公共的,它返回数组
,其中