<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【我與接口】C# IEnumerable、IQueryable 與 LINQ

        學生時期,有過小組作業(yè),當時分工一人做那么兩三個頁面,然而在前端差不多的時候,我和另一個同學發(fā)生了爭執(zhí)。當時用的是簡單的三層架構(DLL、BLL、UI),我個人覺得各寫各的吧,到時候合并,而他覺得應該把底層先寫好,他好直接調(diào)用中間層的方法。

        到出來工作之后,接觸接口,想整理一下這個:接口到底是個什么概念呢?

        需要說明一點的是,我這里說的接口,不是API那個接口,而是“暫時沒實現(xiàn)”那個接口。

        剛接觸接口類型的時候,還不太熟練,看到返回接口類型的方法,總在奇怪,這個返回的對象怎么知道它取哪個實現(xiàn)?可以看一個簡單的例子:

      報錯  

      (無法創(chuàng)建抽象類或接口的實例  

          var test = new ITestInterface();

      正確   

          ITestInterface infa = new TestInterface();

          infa.Func1();

        也即,返回的類型總是具類,是確定的,方法已經(jīng)實現(xiàn)的

      ITestInterface infa = new TestInterface();

         其中的 ITestInterface 更像一個模具,對應這個模具造型的內(nèi)容,由TestInerface提供。

        那么,接口到底如何使用?

        接口的使用,要這樣看:“具備某種特征(功能)”。

        例如看 ITestInterface infa = new TestInterface(); 其中,TestInterface具備有ITestInterface的特征,而ITestInterface作為有某種特征(功能)的標記,它對具體如何達到這種特征(功能)是不感興趣的,有標記就有特征。這種標記的體現(xiàn),在C#里面就是繼承。

        說到這里,老朋友IEnumerable是一定要介紹的。

       

      一、迭代器 IEnumerable

        集合這種數(shù)據(jù)結構是很常見的,通常的操作是對集合的內(nèi)容做篩選,或排序。IEnumerable接口描述的是返回可循環(huán)訪問集合的枚舉數(shù),繼承這個接口,需要實現(xiàn) public IEnumerator GetEnumerator() {} 方法。

        那么,IEnumerator是個什么er?繼承這個接口之后,IDE提示需要實現(xiàn)的方法——

          public class Iterator : IEnumerator
          {
              public object Current => throw new NotImplementedException();
              public bool MoveNext()  { … }
              public void Reset()  { … }
          }

         有一個當前對象,一個是否能指向下一個的判斷,還有一個重置。那么,可以想象迭代器應該是這樣用的:

          Iterator iterator = new Iterator();
          while (iterator.MoveNext())
          {
              // Get iterator.Current to do something..
              Console.WriteLine(iterator.Current.ToString());
          }

        但這看起來,并不太聰明,或者這樣使用比較“合理”:

       

        是不是get到了某種真相?foreach里面接受的是IEnumerable對象,并且會在此處調(diào)用到GetEnumerator去得到Enumerator。那么到底public IEnumerator GetEnumerator(){}要怎么實現(xiàn)呢,C# 2已經(jīng)提供了yield語句簡化迭代器。

          public class IterationSample : IEnumerable
          {
              public IEnumerator GetEnumerator()
              {
                  for (int index = 0; index < values.Length; index++)
                  {
                      yield return values[(index + startingPoint) % values.Length];
                  }
              }
      
              public object[] values;
              public int startingPoint;
      
              public IterationSample(object[] values, int startingPoint)
              {
                  this.values = values;
                  this.startingPoint = startingPoint;
              }
          }

         再來使用Enumerator:

          object[] objs = new object[]{"a", "b", "c", "d"};
          IterationSample sam = new IterationSample(objs, 0);
          foreach (var str in sam)
          {
              // do something..
          }

        可以想象,yield是個怎么樣的存在,“一次一次返回”這是我對yield的第一印象描述。但總覺得還是有些說不清楚,這種時候還是得看看書:

        “yield return 語句指表示 ’暫時地’ 退出方法——事實上,可以把它當做暫停”,

        既然有這種說法,那還得給出個demo[1],關于怎么個“暫停”。

        (這里悄咪咪用C# 6的新語法using static System.Console; 實在懶得打 Console.WriteLine();)

          class Program
          {
              static void Main(string[] args)
              {
                  IEnumerable<int> iterable = CreateEnumerable();
                  IEnumerator<int> iterator = iterable.GetEnumerator();
                  WriteLine("Starting to iterate");
                  while (true)
                  {
                      WriteLine("Calling MoveNext()..");
                      bool result = iterator.MoveNext();
                      WriteLine($"MoveNext result = {result}");
                      if (!result) break;
                      WriteLine("Fetching Current..");
                      WriteLine($"..Current result = {iterator.Current.ToString()}");
                  }
                  ReadLine();
              }
      
              static readonly string Padding = new string(' ', 30);
       
              static IEnumerable<int> CreateEnumerable()
              {
                  WriteLine("Start of CreateEnumerable()");
                  for (int i = 0; i < 2; i++)
                  {
                      WriteLine($"{Padding} About to yield {i}");
                      yield return i;
                      WriteLine($"{Padding} After yield");
                  }
                  WriteLine($"{Padding} Yielding final value");
                  yield return -1;
      
                  WriteLine($"{Padding} End of CreateEnumerable");
              }
          }

       

        此處可以留意“After yield”是什么時候出現(xiàn)的,就會發(fā)現(xiàn)[1]:

         l   在第一次調(diào)用MoveNext之前,CreateEnumerable中的代碼不會被調(diào)用;

         l   當調(diào)用MoveNext時,Current也同時變化;

         l   在yield return的位置,代碼就停止執(zhí)行,在下一次調(diào)用MoveNext時又繼續(xù)執(zhí)行(再return一次)

        yield的故事還沒有完,此處就簡短介紹。

       

        yield return提供了逐個返回的條件,對于僅是取集合當中符合篩選條件的一項,用yield是方便的,逐個返回的情況下,不會占用過多的存儲空間。但如果涉及到排序(或者比大小、最值)的問題,那必然要求集合當中的所有數(shù)據(jù)處于可用狀態(tài),這里也出現(xiàn)了一些傳值的概念。

        yield return屬于延遲執(zhí)行(Deferred Execution),延遲執(zhí)行再區(qū)分為惰性求值(Lazy Evaluation)和熱情求值(Eager Evaluation)。 

      Deferred but eager execution

      Deferred and lazy execution

          IEnumerable<int> GetComputation(int maxIndex)

          {

              var result = new int[maxIndex];

              for(int i = 0; i < maxIndex; i++)

              {

                  result[i] = Computation(i);

              }

              foreach(var value in result)

              {

                  yield return value;

              }

          }

          IEnumerable<int> GetComputation(int maxIndex)

          {

              for(int i = 0; i < maxIndex; i++)

              {

                  yield return Computation(i);

              }

          }

        詳見:https://stackoverflow.com/questions/2515796/deferred-execution-and-eager-evaluation

       

        下面這個例子,是惰性求值,迭代器返回的值受lambda表達式控制,并且是在每一次訪問到這一個“點”的時候,再去返回 “點”的處理結果。熱情求值是直接返回“點”,沒有再過處理。兩相比較,還得看具體的編程情況以作選擇,此處不贅述。

          static void Main(string[] args)
          {
              var sequence = Generate(10, () => DateTime.Now);
              foreach (var value in sequence)
                  WriteLine($"{value:T}");
          }
      
          static IEnumerable<TResult> Generate<TResult>(int number, Func<TResult> generator)
          {
              for (var i = 0; i < number; i++)
              {
                  Sleep(400);
                  yield return generator();
              }
          }

         (為了邏輯上的全面性,)與延遲執(zhí)行相對的是立即執(zhí)行(Immediately Execution),是一次返回就完成函數(shù)的操作。

       

      二、迭代器 IQueryable

        LINQ to Object 是針對本地數(shù)據(jù)存儲(local data store)來執(zhí)行查詢的,系統(tǒng)會根據(jù)lambda表達式里面的邏輯創(chuàng)建匿名的委托,并執(zhí)行代碼;

        LINQ to SQL 針對的是在數(shù)據(jù)庫執(zhí)行的,會把查詢條件解析成T-SQL,并且把SQL語句發(fā)送給數(shù)據(jù)庫引擎。

       

        關于,自動生成SQL語句這一點,可以做個嘗試,例如:創(chuàng)建了一個EF,調(diào)試監(jiān)控連接數(shù)據(jù)庫后返回的變量類型。

          var dbcontext = new CM_FORTESTEntities();
          var tb1 = dbcontext.tblEmployees;
          var tb2 = dbcontext.tblEmployees.Where(a => a.Id == 1);
          var tb3 = dbcontext.tblEmployees.Where(a => a.Gender == "Male").OrderByDescending(a => a.Id);

       

       

       

        咋一看,怎么還能是不同類型?但是再看類成員,會發(fā)現(xiàn)一些端倪:

      public abstract class DbSet : DbQuery, IInternalSetAdapter
      public abstract class DbQuery : IOrderedQueryable, IQueryable, IEnumerable, IListSource, IInternalQueryAdapter
      
      public interface IOrderedQueryable : IQueryable, IEnumerable

       

        好了,終于引入到這個朋友——IQueryable,IQueryable有些什么必要實現(xiàn)的方法呢?

          public class QueryableSample : IQueryable
          {
              public Expression Expression => throw new NotImplementedException();
              public Type ElementType => throw new NotImplementedException();
              public IQueryProvider Provider => throw new NotImplementedException();
              public IEnumerator GetEnumerator()
              {  throw new NotImplementedException(); }
          }

       

        IQueryable是IEnumerable的孩子(IQueryable : IEnumerable),它是一個有自己花樣的迭代器。這個花樣如何體現(xiàn)呢?關鍵還在于Expression、IQueryProvider上。

        從字面上來看,Expression是查詢條件的表達式樹;那么Provider就是提供數(shù)據(jù)的成員了。

          public class QueryableSample : IQueryable
          {
              public Expression Expression { get; }
              public Type ElementType => typeof(ModelItem);
              public IQueryProvider Provider { get; }
      
              public IEnumerator GetEnumerator()
              {
                  return Provider.Execute<IEnumerable>(Expression).GetEnumerator();
              }
      
              IEnumerator IEnumerable.GetEnumerator()
              {
                  return GetEnumerator();
              }
      
              public QueryableSample(IQueryProvider provider, Expression expression)
              {
                  if (provider == null)
                      throw new ArgumentNullException("provider");
                  if (expression == null)
                      throw new ArgumentNullException("expression");
      
                  Provider = provider;
                  Expression = expression;
              }
          }
      View Code

        預感中,Provider會是個重要角色:

      public class QueryProvider : IQueryProvider

      IQueryable CreateQuery(Expression expression)

      return new QueryableSample(this, expression);

      IQueryable<TElement> CreateQuery<TElement>(Expression expression)

      return (IQueryable<TElement>) new QueryableSample(this, expression);

      object Execute(Expression expression)

      return QueryResult.Execute(expression, false);

      TResult Execute<TResult>(Expression expression)

      bool IsEnumerable = (typeof(TResult).Name == "IEnumerable`1");

      return (TResult)QueryResult.Execute(expression, IsEnumerable);

       

          public class QueryProvider : IQueryProvider
          {
              public IQueryable CreateQuery(Expression expression)
              {
                  return new QueryableSample(this, expression);
              }
              public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
              {
                  return (IQueryable<TElement>) new QueryableSample(this, expression);
              }
              public object Execute(Expression expression)
              {
                  return QueryResult.Execute(expression, false);
              }
              public TResult Execute<TResult>(Expression expression)
              {
                  bool IsEnumerable = (typeof(TResult).Name == "IEnumerable`1");
                  return (TResult)QueryResult.Execute(expression, IsEnumerable);
              }
          }
          public sealed class QueryResult
          {
              public static object Execute(Expression expression, bool isEnumerable)
              { // 利用expression得到數(shù)據(jù)結果,設其為records
                  QueryableSample records = null;
                  if (isEnumerable)
                      return records.Provider.CreateQuery(expression);
                  else
                      return records.Provider.Execute(expression);
              }
          }
      View Code

       

        在github上找到了個詳盡些的QueryableDemo可以看: https://github.com/andreychizhov/NestQueryableProvider

       

      三、IEnumerable 與 IQueryable

         下面以一個例子比較二者最大的區(qū)別[2]:

                  var q = from c in dbContext.Customers

                             where c.City == "London"

                             select c;

                  var finalAnswer = from c in q

                                            orderby c.Name

                                            select c;

       

      使用IQueryable<T>所內(nèi)置的LINQ to SQL機制。

      (LINQ to SQL程序庫會把相關的查詢操作合起來執(zhí)行,僅向數(shù)據(jù)庫發(fā)出一次調(diào)用,即where和orderby都是在同一次SQL查詢中完成。)

                  var q = (from c in dbContext.Customers

                              where c.City == "London"

                              select c).AsEnumerable();

                  var finalAnswer = from c in q

                                            orderby c.Name

                                            select c;

       

      把數(shù)據(jù)庫對象強制轉(zhuǎn)換成IEnumerable形式的序列,并把排序等工作放在本地完成。

      (即會把where字句后得到的結果轉(zhuǎn)換成IEnumerable<T>的序列,再采用LINQ to Objects機制完成后續(xù),排序是通過委托在本地執(zhí)行。)

        注意:

        兩種不同的數(shù)據(jù)處理方式,依循著兩套完全不同的流程。無論是用lambda表達式來撰寫查詢邏輯還是以函數(shù)參數(shù)的形式來表示這些邏輯,針對IEnumerable<T>所設計的那些擴展方法都將其視為委托。反之,針對IQueryable<T>的那些擴展方法用的則是表達式樹。【表達式樹 可以把各種邏輯合并起來成一條SQL語句。】

       

      public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
      public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)

       

        如果使用IEnumerable<T>,則必須在本地進行。系統(tǒng)把lambda表達式編譯到方法里,在本地計算機上運行,這意味著無論有待處理的數(shù)據(jù)在不在本地,都必須先獲取過來才行。

        同時,用來支持IQueryable的那些Provider未必能夠完全解析每一種查詢,通常這些Provider只能解讀幾種固定的(.NET Framework已經(jīng)實現(xiàn))的運算符(方法),如果要在查詢操作里面調(diào)用除此之外的其它方法,那可能就得把序列當成IEnumerable來查詢。

       

      吐槽    :emmmmmm,,,本來是想寫我與接口二三事,結果竟然如此跑偏,太多細節(jié)能扣啦,知識點冥冥間也有關聯(lián),慢慢捋吧~

      立Flag:本月開啟機器學習,今年要把C#基礎篇搞定。

       

      注釋:

      [1] 自《深入理解C#》(第3版)Jon Skeet 著  姚琪琳 譯

      [2] 自《Effective C#》(第3版) 比爾·瓦格納 著

       

      posted @ 2019-05-10 10:39  Carcar019  閱讀(677)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 国产一区二区三区高清视频| 无人区码一码二码三码区 | 99久久精品一区二区国产| 91区国产福利在线观看午夜| 中文毛片无遮挡高潮免费| 欧美牲交a欧美牲交aⅴ免费| 亚洲精品一二三伦理中文| 91蜜臀国产自产在线观看| 2021国产成人精品久久| 中文字幕av日韩有码| 日韩精品人妻av一区二区三区| 亚洲 a v无 码免 费 成 人 a v| 日韩人妻无码一区二区三区| 亚洲成av人片不卡无码手机版| 成人特黄A级毛片免费视频 | 国产网友愉拍精品视频手机 | 久久久久久久久久久久中文字幕| 国产一级av在线播放| 最新亚洲av日韩av二区| 亚洲aⅴ男人的天堂在线观看| 国产精品一精品二精品三| 极品人妻少妇一区二区| 廊坊市| 91色老久久精品偷偷蜜臀 | 午夜色无码大片在线观看免费| 国产一区日韩二区欧美三区| 亚洲这里只有久热精品伊人| 虎白女粉嫩尤物福利视频| 风流老熟女一区二区三区| 亚洲欧洲日产国码久在线| 精品精品国产国产自在线| 国产亚洲人成网站在线观看| 国产破外女出血视频| 亚洲国产中文字幕在线视频综合| 人妻少妇邻居少妇好多水在线 | 日韩精品人妻黄色一级片| 老司机精品成人无码av| 日韩乱码人妻无码中文字幕视频 | 国产中文字幕精品喷潮| 国产午夜福利视频在线观看| 制服丝袜人妻有码无码中文字幕|