最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C# yield在WCF中的錯誤使用(二)

 更新時間:2015年04月10日 11:17:53   投稿:junjie  
這篇文章主要介紹了C# yield在WCF中的錯誤使用(二),本文講解的內(nèi)容據(jù)說是99%的開發(fā)人員都有可能犯的錯誤,需要的朋友可以參考下

昨天寫了《yield在WCF中的錯誤使用——99%的開發(fā)人員都有可能犯的錯誤[上篇]》,引起了一些討論。關(guān)于yield關(guān)鍵字這個語法糖背后的原理(C#編譯器將它翻譯成什么)其實挺簡單,雖然有時候因為誤用它會導(dǎo)致一些問題,但是它本無過錯。接下來,我們通過這篇短文簡單地談?wù)勎宜斫獾膟ield。

目錄

一、先看一個簡單的例子
二、了解本質(zhì),只需要看看yield最終編譯成什么
三、回到WCF的例子

一、先看一個簡單的例子
我們現(xiàn)在看一個簡單的例子。我們在一個Console應(yīng)用中編寫了如下一段簡單的程序:返回類型為IEnumerable<string>的方法GetItems以yield return的方式返回一個包含三個字符串的集合,而在方法開始的時候我們打印一段文字表明定義在方法中的操作開始執(zhí)行。在Main方法中,我們先調(diào)用GetItems方法將“集合對象”返回,然后調(diào)用其ToArray方法。在調(diào)用該方法之前我們打印一段文字表明對集合對象進(jìn)行迭代。

復(fù)制代碼 代碼如下:

static void Main(string[] args)
{
    IEnumerable<string> items = GetItems();
    Console.WriteLine("Begin to iterate the collection.");
    items.ToArray();
}

static IEnumerable<string> GetItems()
{
    Console.WriteLine("Begin to invoke GetItems() method");
    yield return "Foo";
    yield return "Bar";
    yield return "Baz";
}

對于上面這段代碼,我想肯定有人會認(rèn)為得到的結(jié)果應(yīng)該是這樣:

復(fù)制代碼 代碼如下:

Begin to invoke GetItems() method
Begin to iterate the collection.

但是下面才是真正的執(zhí)行結(jié)果。也就是說,一旦我們在一個返回類型為IEnumerable或者IEnumerable<T>的方式中通過yield return返回集合元素,意味著這個定義在方法中操作會被“延后執(zhí)行”——操作的真正執(zhí)行不是發(fā)生在方法調(diào)用的時候,而是延后到對返回的集合進(jìn)行迭代的時候。我們大體可以以這樣的方式來“解釋”這個現(xiàn)象:一旦我們使用了yield return,返回元素的操作會被封裝成“可執(zhí)行的表達(dá)式”的方式返回,一旦我們對集合進(jìn)行迭代的時候,這些表達(dá)式才會被執(zhí)行。
復(fù)制代碼 代碼如下:

Begin to iterate the collection.
Begin to invoke GetItems() method

二、了解本質(zhì),只需要看看yield最終編譯成什么

上面我們通過“延遲執(zhí)行”和“可執(zhí)行表達(dá)式”的形式來解釋yield return,僅僅是為了比較好地理解它所體現(xiàn)出來的效果而已,實際上并沒有這回事,這與LINQ的延遲加載更不是一回事。yield return僅僅是C#的一個語法糖而已,是編譯器玩的一個小花招。如何透過這一層“糖紙”看到本質(zhì)的東西,只需要看看編譯器最終編譯后的與之等效的代碼是什么樣子就可以了。對于上面這個例子來說,不管GetItems方法中以何種方式返回需要的對象,返回值總歸是一個實現(xiàn)了IEnumerable <string>接口的某個類型的對象,我們只需要看看這個類型具有怎樣的定義就知道C#編譯器如果來“解釋”yield return。

我們可以直接利用Reflector打開編譯后的程序集,然后將.NET Framework的版本調(diào)成1.0(不支持C#針對后續(xù)版本提供的語法糖),這樣就可以以“本質(zhì)”的方式查看我們編寫的代碼了。如下面的代碼片段所示,GetItems方法中沒有發(fā)現(xiàn)我們定義的代碼,而是直接返回一個類型為<GetItems>d__0的對象,看到這里相信讀者朋友們知道為什么執(zhí)行GetItems方法的時候并沒有文字輸出的真正原因了吧。

復(fù)制代碼 代碼如下:

internal class Program
{
    private static IEnumerable<string> GetItems()
    {
        return new <GetItems>d__0(-2);
    }
    private sealed class <GetItems>d__0 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
}

<GetItems>d__0是自動生成的類型,它實現(xiàn)了IEnumerable<string>接口,也實現(xiàn)了IEnumerator<string>,其 GetEnumerator()方法返回的實際上就是他自己。至于對<GetItems>d__0對象的進(jìn)行迭代的時候如何返回具體元素,只要看看該類型的定義就一目了然了。如下面的代碼片段所示,集合元素的返回實現(xiàn)在MoveNext()方法中,方法開始的操作(Console.WriteLine("Begin to invoke GetItems() method"))發(fā)生在第一次迭代的時候。
復(fù)制代碼 代碼如下:

private sealed class <GetItems>d__0 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
{
    private int <>1__state;
    private string <>2__current;

    private bool MoveNext()
    {
        switch (this.<>1__state)
        {
            case 0:
                this.<>1__state = -1;
                Console.WriteLine("Begin to invoke GetItems() method");
                this.<>2__current = "Foo";
                this.<>1__state = 1;
                return true;

            case 1:
                this.<>1__state = -1;
                this.<>2__current = "Bar";
                this.<>1__state = 2;
                return true;

            case 2:
                this.<>1__state = -1;
                this.<>2__current = "Baz";
                this.<>1__state = 3;
                return true;

            case 3:
                this.<>1__state = -1;
                break;
        }
        return false;
    }
    string IEnumerator<string>.Current
    {
        [DebuggerHidden]
        get
        {
            return this.<>2__current;
        }
    }

    object IEnumerator.Current
    {
        [DebuggerHidden]
        get
        {
            return this.<>2__current;
        }
    }
}

三、回到WCF的例子

再次回到《yield在WCF中的錯誤使用——99%的開發(fā)人員都有可能犯的錯誤[上篇]》中提到的例子,現(xiàn)在來解釋為什么針對如下兩段代碼,前者拋出的異常不能被WCF正常處理,而后者可以。原因很簡單——兩段代碼拋出異常的時機是不一樣的。對于后者,異常在執(zhí)行GetItems方法的時候會立即拋出來,WCF會捕獲這個異常并作為應(yīng)用級別的異常進(jìn)行正常處理;對于前者,通過上面的分析我們知道異常實際上發(fā)生在對返回“集合對象”進(jìn)行迭代的時候。具體是什么時候呢?其實就是對返回對象進(jìn)行序列化的時候,此時拋出的異常將將會視為系統(tǒng)異常來處理。

復(fù)制代碼 代碼如下:

public class DemoService : IDemoService
{
    public IEnumerable<string> GetItems(string categoty)
    {
        if (string.IsNullOrEmpty(categoty))
        {
            throw new FaultException("Invalid category");
        }
        yield return "Foo";
        yield return "Bar";
        yield return "Baz";
    }
}

public class DemoService : IDemoService
{
    public IEnumerable<string> GetItems(string categoty)
    {
        if (string.IsNullOrEmpty(categoty))
        {
            throw new FaultException("Invalid category");
        }
        return new string[] { "Foo", "Bar", "Baz" };
    }
}


我個人覺得這是WCF值得改進(jìn)的地方,但是目前來說為了避免這樣的問題,我推薦將WCF契約接口操作方法中的返回類型定義成數(shù)組,而不是IEnumerable或者IEnumerable<T>(順便說一下,WCF針對Array、List以及其他集合類型的序列化/反序列化行為是一致的),但是我個人對IEnumerable或者IEnumerable<T>不排斥。

相關(guān)文章

  • c#操作sql server2008 的界面實例代碼

    c#操作sql server2008 的界面實例代碼

    這篇文章主要介紹了c#操作sql server2008 的界面實例代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-03-03
  • C#中WPF使用多線程調(diào)用窗體組件的方法

    C#中WPF使用多線程調(diào)用窗體組件的方法

    這篇文章主要介紹了C#中WPF使用多線程調(diào)用窗體組件的方法,涉及C#中多線程的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-04-04
  • Unity中使用反射機制調(diào)用函數(shù)

    Unity中使用反射機制調(diào)用函數(shù)

    這篇文章主要為大家詳細(xì)介紹了Unity中使用反射機制調(diào)用函數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • C#實現(xiàn)由四周向中心縮小的窗體退出特效

    C#實現(xiàn)由四周向中心縮小的窗體退出特效

    這篇文章主要介紹了C#實現(xiàn)由四周向中心縮小的窗體退出特效,通過簡單的C#窗口調(diào)用參數(shù)設(shè)置實現(xiàn)該退出特效功能,非常簡單實用,需要的朋友可以參考下
    2015-08-08
  • C#中多線程ManualResetEvent 與 AutoResetEvent 區(qū)別

    C#中多線程ManualResetEvent 與 AutoResetEvent 區(qū)別

    這篇文章主要介紹了C#中ManualResetEvent 與 AutoResetEvent 區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • C#使用XML序列化操作菜單的方法

    C#使用XML序列化操作菜單的方法

    這篇文章主要介紹了C#使用XML序列化操作菜單的方法,是基于上一篇遞歸讀取XML菜單數(shù)據(jù)的改進(jìn)方法,具有一定的參考借鑒價值,需要的朋友可以參考下
    2014-12-12
  • C# goto語句的具體使用

    C# goto語句的具體使用

    # goto 語句用于直接在一個程序中轉(zhuǎn)到程序中的標(biāo)簽指定的位置,標(biāo)簽實際上由標(biāo)識符加上冒號構(gòu)成。本文主要介紹了C# goto語句的具體使用,感興趣的可以了解一下
    2021-06-06
  • 日常收集C#接口知識(知識全面)

    日常收集C#接口知識(知識全面)

    本文分為七章節(jié)給大家介紹c#接口知識,內(nèi)容比較詳細(xì),特此分享腳本之家平臺,供大家參考
    2016-01-01
  • C#使用LINQ查詢文件列表并找出最大文件

    C#使用LINQ查詢文件列表并找出最大文件

    在現(xiàn)代 C# 開發(fā)中,LINQ (Language Integrated Query) 提供了一種強大而優(yōu)雅的方式來處理集合數(shù)據(jù),本文將詳細(xì)介紹如何使用 LINQ 查詢文件系統(tǒng)中的文件,并找出最大的文件數(shù)量,需要的朋友可以參考下
    2024-10-10
  • C#事件訂閱發(fā)布實現(xiàn)原理詳解

    C#事件訂閱發(fā)布實現(xiàn)原理詳解

    這篇文章主要介紹了C#事件訂閱發(fā)布實現(xiàn)原理詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12

最新評論

平果县| 建宁县| 舒城县| 临漳县| 五寨县| 鸡泽县| 廊坊市| 响水县| 鱼台县| 垦利县| 横峰县| 岑溪市| 凤阳县| 岳阳市| 尤溪县| 乌拉特中旗| 苏尼特右旗| 宝山区| 黄山市| 台安县| 聂拉木县| 临潭县| 临沧市| 万载县| 搜索| 定边县| 浑源县| 揭西县| 周宁县| 通渭县| 宁陵县| 元氏县| 吉木萨尔县| 桃园市| 新余市| 凤庆县| 图片| 沂水县| 安顺市| 靖安县| 清新县|