Use of Where in C #

1, Restraint

Constraint types include base class constraints, constructor constraints, interface constraints, parameter constraints, etc. For example:

public class FateherTest
{
}
//Usage of where: interface constraint IComparable and constructor constraint new(), base class constraint FatherTest
public class TestA<T> where T : FateherTest, IComparable, new()
{
}

public class TestB
{
 //Restricting the type of the passed parameter must inherit the IComparable parameter type constraint
    public int Caculate<T>(T t) where T : IComparable
    {
        throw new NotImplementedException();
    }
}

2, Set condition query

1. Usage

  List<InstanceB> list = new List<InstanceB>();

  //Return the IEnumerable object whose value of b.value = = 2

  list.Where((b) => b.value == 2).ToList();

  Dictionary<int, InstanceB> dic = new Dictionary<int, InstanceB>();

  //TSource is keyvaluepair < tkey, tvalue >

  dic.Where((b) => b.Value.value== 2);

In system The Enumerable class under LINQ namespace implements the extension method of IEnumerable As follows:

 //The return value is the IEnumerable < T > iterator object

 public static IEnumerable<TSource> Where<TSource>(

      this IEnumerable<TSource> source,

      Func<TSource, bool> predicate)

    {

      if (source == null)

        throw Error.ArgumentNull(nameof (source));

      if (predicate == null)

        throw Error.ArgumentNull(nameof (predicate));

      switch (source)

      {

        case Enumerable.Iterator<TSource> _:

          return ((Enumerable.Iterator<TSource>) source).Where(predicate);

        case TSource[] _:

          return (IEnumerable<TSource>) new Enumerable.WhereArrayIterator<TSource>((TSource[]) source, predicate);

        case List<TSource> _:

          return (IEnumerable<TSource>) new Enumerable.WhereListIterator<TSource>((List<TSource>) source, predicate);

        default:

          return (IEnumerable<TSource>) new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);

      }

    }

So when we use the Where extension method of geometry:

For list:

List.where  reurn  (IEnumerable<TSource>) new Enumerable. whereListIterator < tSource > ((tSource []) source, predicate) returns a whereListIterator object. Where {tSource is the InstanceB type

We can see the class WhereListIterator , which inherits enumerable Iterator<TResult>: IEnumerable<TSource> . It implements the function of iterator. Check the MoveNext method:

  public override bool MoveNext()

      {

        if (this.state == 1)

        {

          while (this.index < this.source.Length)

          {

            TSource source = this.source[this.index];

            ++this.index;

            if (this.predicate(source))

            {

              this.current = source;

              return true;

            }

          }

          this.Dispose();

        }

        return false;

      }

MoveNext will return true only after passing the predict detection When calling Tolist, the callback uses the extension method of ienumtable to create a list < T > object to return. The constructor is initialized with a collection, as follows:
 

    [__DynamicallyInvokable]

    public List(IEnumerable<T> collection)

    {

      if (collection == null)

        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);

      if (collection is ICollection<T> objs)

      {

        int count = objs.Count;

        if (count == 0)

        {

          this._items = List<T>._emptyArray;

        }

        else

        {

          this._items = new T[count];

          objs.CopyTo(this._items, 0);

          this._size = count;

        }

      }

      else

      {

        this._size = 0;

        this._items = List<T>._emptyArray;

        foreach (T obj in collection)

          this.Add(obj);

      }

    }

Using foreach above will call MoveNext. Of WhereListIterator Until false is returned. MoveNext above is used to filter Func delegate functions.

We can also do the following:

var  whereListIt  = list.Where((b) => b.value >1) 

 Func<InstanceB,InstanceB> func = delegate (InstanceB b)

 {

     b.value = 3;

     return b;

  };

newList = whereListIt.Select(func).ToList();

Filter out the exclusive b.value > 1 for the first time, and set the value of the filtered objects to 3 for the second time

  whereListIt. Select((b)=> b.value >1); The returned object type is: WhereSelectListIterator, which still inherits enumerable Iterator<TResult>. The implementation of its iterator MoveNext is as follows:

 

      public override bool MoveNext()

      {

        if (this.state == 1)

        {

          while (this.index < this.source.Length)

          {

            TSource source = this.source[this.index];

            ++this.index;

            if (this.predicate == null || this.predicate(source))

            {

//It can be seen here that the Selector operates on the source, not filtering.

              this.current = this.selector(source);

              return true;

            }

          }

          this.Dispose();

        }

        return false;

      }

The inheritance structure is as follows:

  1. WhereListIterator:Enumerable.Iterator<TResult>: IEnumerable<TSource>
  2. WhereListIterator:Enumerable.Iterator<TResult>: IEnumerable<TSource>

There can be continuous operations, such as:

list.Where((b) => b.value == 2).where((b) => b.value == 2)

  Func<InstanceB,InstanceB> func = delegate (InstanceB b)

        {

            b.value = 3;

            return b;

        };



list.Select(t).Select(t)

There can also be combined operations

list.Where((b) => b.value == 2).Select(t)

The reason is that continuous operation is done internally Combine Operation: here is Selector call Selector Namely: Selector.Selector

     public override IEnumerable<TResult2> Select<TResult2>(

        Func<TResult, TResult2> selector)

      {

        return (IEnumerable<TResult2>) new Enumerable.SelectEnumerableIterator<TSource, TResult2>(this._source, Enumerable.CombineSelectors<TSource, TResult, TResult2>(this._selector, selector));

      }

The combination operation is combined: here is Selector.Where Combined operation of

public override IEnumerable<TResult> Where(Func<TResult, bool> predicate) => (IEnumerable<TResult>) new Enumerable.WhereEnumerableIterator<TResult>((IEnumerable<TResult>) this, predicate);

//The yellow part above passes This current object to the next object, whereenumerable iterator. The operations of whereenumerable iterator in MoveNext are as follows:

Take the iterator of the last object and handle it first. Call MoveNext of the previous object, call Func of the Selector, and then call this Predict for screening.

    • Thinking about structure from the perspective of design pattern

For List, Dictionary and Array, these data structures all implement the interface of IEnumable iterator, so traversal can be realized.

Now you have to add filtering to these data sets There are two implementation methods:

  1. Write a new interface to implement these collections, and use the iterative function to implement filtering.

The disadvantage is that an object needs to implement this method. It destroys the structure of the original class.

        2. Because the filter function only uses iterators, you can

First: use the extension method to extend the function of ienumtable without modifying the content of ienumtable interface.

Second: implement new class objects, hold ienumtable objects for iterative processing, and use combination instead of inheritance.

Third: the newly created objects represent different operations and inherit the same parent class. Therefore, different objects can be combined with each other using the combination mode to realize complex operations.

 

Keywords: C#

Added by teomanersan on Sat, 15 Jan 2022 03:54:43 +0200