Thứ ba, ngày 21 tháng 11 năm 2017

LINQ – Deferred operator và cơ chế thực hiện truy vấn

Ngày đăng: 12/3/2012, 13:55:36AM | Lượt xem: 2,093
Hot!

Các toán tử (extension method) trong LINQ được chia thành hai loại theo cách mà chúng được thực thi: deferred và non-deferred. Việc hiểu mà phân biệt được các toán tử này là một vấn đề cần thiết vì bạn có thể nhận được một kết quả ngoài dự đoán khi sử dụng nếu không cẩn thận.

Các toán tử (extension method) trong LINQ được chia thành hai loại theo cách mà chúng được thực thi: deferred và non-deferred. Việc hiểu mà phân biệt được các toán tử này là một vấn đề cần thiết vì bạn có thể nhận được một kết quả ngoài dự đoán khi sử dụng nếu không cẩn thận.

 

Cơ chế thực hiện truy vấn và các deferred operator

Trước tiên bạn hãy xem một ví dụ đơn giản sau và xem kết quả được xuất ra:

public static void Main(){
    List<string> list= new List<string>(){
        "Olivia",
        "Ruby",
        "Sophie",
    };

    IEnumerable<string> items = from n in list
        where n.Length==4
        select n;

    foreach(var item in items)
        Console.WriteLine(item);

    Console.WriteLine("---------------");
    list.Add("Lily");

    foreach(var item in items)
        Console.WriteLine(item);

    Console.Read();
}

Output:

Ruby
—————
Ruby
Lily

Ví dụ trên dùng để xuất các tên có độ dài bằng 4. Như bạn thấy, mặc dù hoàn toàn không thay đổi gì liên quan đến biến items giữa hai lần xuất kết quả, nhưng kết quả xuất ra của hai lần lại khác nhau khi ta thêm một phần tử vào list.

Từ ví dụ này ta có thể suy ra mỗi lần được sử dụng, câu query sẽ được thực thi lại. Trong LINQ, khi bạn tạo một câu query và gán cho một đối tượng, thì lúc đó câu query này sẽ được lưu lại dưới dạng delegate Func<> trong một static field của lớp hiện tại, trong trường hợp này là Func<string,bool>.

Note: Để dễ thấy bạn có chuyển sang cú pháp viết bằng phương thức, tham số của phương thức Where() có thể là Func<T,bool> hoặc Func<T,int,bool>, với T là kiểu dữ liệu của các phần tử trong collection mà bạn sử dụng phương thức này (generic):

IEnumerable<string> items = list.Where( n => n.Length==4);

Trong LINQ, cơ chế này được gọi là “query plan” và các toán tử hoạt động theo cơ chế này được goi là “deferred operator”. Các toán tử còn lại được gọi là “non-deferred operator”.

Nhận biết các toán  tử này như thế, nào? Rất đơn giản, các toán tử deferred có kiểu trả về là IEnumerable<T> hoặc IOrderedEnumerable<T>

Có những trường hợp bạn muốn lấy toàn bộ kết quả của truy vấn, hãy sử dụng toán tử ToList, ToArray hoặc ToDictionary để chuyển chúng về một List<T> hoặc mảng. Bạn không phải lo lắng đến hiệu suất vì nghĩ rằng chúng sẽ thực hiện lặp lại nhiều lần để thực hiện truy vấn và tạo collection. Cơ chế thực hiện bên trong các toán tử  sẽ giúp tối ưu mà tôi sẽ bàn đến ngay trong phần sau đây.

Note: Func<> và IEnumerable<T> không phải là cách duy nhất mà LINQ sử dụng để lưu trữ và thực hiện truy vấn. Bạn sẽ được tìm hiểu về IQueryable<T> và lớp này sử dụng Expression Tree để lưu trữ câu truy vấn thay cho Func<>. Vấn đề này sẽ được giới thiệu trong một bài viết khác.

Cơ chế yielding và các toán tử trong LINQ

Chuyển qua một ví dụ khác ta có thể thấy cách mà phương thức Where() này được viết.

public static void Main(){
    string[] numbers={"1","3","five"};
    try{
        IEnumerable<string> items =  from  n in numbers
            where int.Parse(n)<5
            select n;

        foreach(var item in items)
            Console.WriteLine(item);

    }catch(Exception ex)
    {
        Console.WriteLine("Runtime exception: "+ex.Message);
    }

    Console.Read();
}

Output:

1
3
Runtime exception: Input string was not in a correct format.

Câu lệnh thực hiện query trong ví dụ trên xảy ra lỗi do chuyển đổi chuỗi “five” thành một số Int32. Theo cách thông thường thì lẽ ra câu lệnh này phải ném ra ngoại lệ trước khi vòng lặp foreach được thực hiện. Tuy nhiên theo kết quả xuất ra, hai giá trị đầu hợp lệ là 1 và 3 vẫn được xuất ra màn hình.

Để làm được điều này, phương thức Where() sử dụng kĩ thuật yielding để trả về lần lượt từng phần tử khi nó được cần đến, thay vì thực hiện và trả về tất cả kết quả một lượt. Điều này giúp cho tốc độ thực hiện truy vấn nhanh hơn và kết quả có được gần như tức thời, đặc biệt hữu ích trong những trường hợp dữ liệu của bạn quá lớn.

Dựa vào đây ta có thể suy ra được nội dung của phương thức Where() tương tự như sau:

public static IEnumerable<T> Where<T>(this IEnumerable<T> source,Func<T,bool> predicate)
{
    foreach(var item in source)
    {
        if(predicate(item))
            yield return item;
    }
}

Để kiểm chứng bạn có thể thực thi ví dụ sau, kết quả xuất ra tương tự như ví dụ trên:

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

namespace LinqExample{
    public static class Program
    {
        public static void Main(){
            string[] numbers={"1","3","five"};
            try{
                IEnumerable<string> items = numbers.Y2Where(n => int.Parse(n) < 5);

                foreach(var item in items)
                    Console.WriteLine(item);

            }catch(Exception ex)
            {
                Console.WriteLine("Runtime exception: "+ex.Message);
            }

            Console.Read();
        }

    }
    public static class Y2QueryEx{
        public static IEnumerable<T> Y2Where<T>(this IEnumerable<T> source,Func<T,bool> predicate)
        {
            foreach(var item in source)
            {
                if(predicate(item))
                    yield return item;
            }
        }
    }
}

Output:

1
3
Runtime exception: Input string was not in a correct format.

Danh sách query operator theo thứ tự alphabet

By operator:

Operator Category

Deferred

Aggregate Aggregate
All Quantifiers
Any Quantifiers
AsEnumerable Conversion

Yes

Average Aggregate
Cast Conversion

Yes

Concat Concatenation

Yes

Contains Quantifiers
Count Aggregate
DefaultIfEmpty Element

Yes

Distinct Set

Yes

ElementAt Element
ElementAtOrDefault Element

Yes

Empty Generation

Yes

Except Set

Yes

First Element
FirstORDefault Element
GroupBy Grouping

Yes

GroupJOin Join

Yes

Intersect Set

Yes

Join Join

Yes

Last Element
LastOrDefault Element
LongCount Aggregate
Max Aggregate
Min Aggregate
OfType Conversion

Yes

OrderBy Ordering

Yes

OrderByDescending Ordering

Yes

Range Generation

Yes

Reverse Ordering

Yes

Repeat Generation

Yes

Select Projection

Yes

SelectMany Projection

Yes

SequenceEqual Equality
Single Element
SingleOrDefault Element
Skip Partitioning

Yes

SkipWhile Partitioning

Yes

Sum Aggregate
Take Partitioning

Yes

TakeWhile Partitioning

Yes

ThenBy Ordering

Yes

ThenByDescending Ordering

Yes

ToDictionary Conversion
ToArray Conversion
ToList Conversion
ToLookup Conversion
Union Set

Yes

Where Restriction

Yes

By category:

Category Operator

Deferred

Aggregate Aggregate
Average
Count
LongCount
Max
Min
Sum
Concatenation Concat

Yes

Conversion AsEnumerable

Yes

Cast

Yes

Conversion OfType

Yes

ToDictionary
ToArray
ToList
ToLookup
Element DefaultIfEmpty

Yes

ElementAt
ElementAtOrDefault

Yes

First
FirstORDefault
Last
LastOrDefault
Single
SingleOrDefault
Equality SequenceEqual
Generation Empty

Yes

Range

Yes

Repeat

Yes

Grouping GroupBy

Yes

Join GroupJOin

Yes

Join

Yes

Ordering OrderBy

Yes

OrderByDescending

Yes

Reverse

Yes

ThenBy

Yes

ThenByDescending

Yes

Partitioning Skip

Yes

SkipWhile

Yes

Take

Yes

TakeWhile

Yes

Projection Select

Yes

SelectMany

Yes

Quantifiers All
Any
Contains
Restriction Where

Yes

Set Distinct

Yes

Except

Yes

Intersect

Yes

Union

Yes

 Chia sẻ qua: 
Hot!
Ý kiến bạn đọc

These items will be permanently deleted and cannot be recovered. Are you sure?

Gallery

image

Maecenas viverra rutrum pulvinar

Maecenas viverra rutrum pulvinar! Aenean vehicula nulla sit amet metus aliquam et malesuada risus aliquet. Vestibulum rhoncus, dolor sit amet venenatis porta, metus purus sagittis nisl, sodales volutpat elit lorem…

Read more

Text Links

Thiết kế logo chuyên nghiệp Insky
DAFABET
W88 w88b.com/dang-ky-tai-khoan-w88
W88
ca do bong da online
Copyright © 2011 - 2012 vietshare.vn by phamkhuong102@gmail.com doanhkisi2315@gmail.com. All rights reserved.