C# yield在WCF中的毛病應用(二)。本站提示廣大學習愛好者:(C# yield在WCF中的毛病應用(二))文章只能為提供參考,不一定能成為您想要的結果。以下是C# yield在WCF中的毛病應用(二)正文
昨天寫了《yield在WCF中的毛病應用——99%的開辟人員都有能夠犯的毛病[上篇]》,惹起了一些評論辯論。關於yield症結字這個語法糖面前的道理(C#編譯器將它翻譯成甚麼)其實挺簡略,固然有時刻由於誤用它會招致一些成績,然則它本無錯誤。接上去,我們經由過程這篇短文簡略地談談我所懂得的yield。
目次
1、先看一個簡略的例子
2、懂得實質,只須要看看yield終究編譯成甚麼
3、回到WCF的例子
1、先看一個簡略的例子
我們如今看一個簡略的例子。我們在一個Console運用中編寫了以下一段簡略的法式:前往類型為IEnumerable<string>的辦法GetItems以yield return的方法前往一個包括三個字符串的聚集,而在辦法開端的時刻我們打印一段文字注解界說在辦法中的操作開端履行。在Main辦法中,我們先挪用GetItems辦法將“聚集對象”前往,然後挪用其ToArray辦法。在挪用該辦法之前我們打印一段文字注解對聚集對象停止迭代。
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";
}
關於下面這段代碼,我想確定有人會以為獲得的成果應當是如許:
Begin to invoke GetItems() method
Begin to iterate the collection.
然則上面才是真實的履行成果。也就是說,一旦我們在一個前往類型為IEnumerable或許IEnumerable<T>的方法中經由過程yield return前往聚集元素,意味著這個界說在辦法中操作會被“延後履行”——操作的真正履行不是產生在辦法挪用的時刻,而是延後到對前往的聚集停止迭代的時刻。我們年夜體可以以如許的方法來“說明”這個景象:一旦我們應用了yield return,前往元素的操作會被封裝成“可履行的表達式”的方法前往,一旦我們對聚集停止迭代的時刻,這些表達式才會被履行。
Begin to iterate the collection.
Begin to invoke GetItems() method
2、懂得實質,只須要看看yield終究編譯成甚麼
下面我們經由過程“延遲履行”和“可履行表達式”的情勢來說明yield return,僅僅是為了比擬好地輿解它所表現出來的後果罷了,現實上並沒有這回事,這與LINQ的延遲加載更不是一回事。yield return僅僅是C#的一個語法糖罷了,是編譯器玩的一個小把戲。若何透過這一層“糖紙”看到實質的器械,只須要看看編譯器終究編譯後的與之等效的代碼是甚麼模樣便可以了。關於下面這個例子來講,不論GetItems辦法中以何種方法前往須要的對象,前往值總歸是一個完成了IEnumerable <string>接口的某個類型的對象,我們只須要看看這個類型具有如何的界說就曉得C#編譯器假如來“說明”yield return。
我們可以直接應用Reflector翻開編譯後的法式集,然後將.NET Framework的版本調成1.0(不支撐C#針對後續版本供給的語法糖),如許便可以以“實質”的方法檢查我們編寫的代碼了。以下面的代碼片斷所示,GetItems辦法中沒有發明我們界說的代碼,而是直接前往一個類型為<GetItems>d__0的對象,看到這裡信任讀者同伙們曉得為何履行GetItems辦法的時刻並沒有文字輸入的真正緣由了吧。
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是主動生成的類型,它完成了IEnumerable<string>接口,也完成了IEnumerator<string>,其 GetEnumerator()辦法前往的現實上就是他本身。至於對<GetItems>d__0對象的停止迭代的時刻若何前往詳細元素,只需看看該類型的界說就了如指掌了。以下面的代碼片斷所示,聚集元素的前往完成在MoveNext()辦法中,辦法開端的操作(Console.WriteLine("Begin to invoke GetItems() method"))產生在第一次迭代的時刻。
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;
}
}
}
3、回到WCF的例子
再次回到《yield在WCF中的毛病應用——99%的開辟人員都有能夠犯的毛病[上篇]》中提到的例子,如今來說明為何針對以下兩段代碼,前者拋出的異常不克不及被WCF正常處置,爾後者可以。緣由很簡略——兩段代碼拋出異常的機會是紛歧樣的。關於後者,異常在履行GetItems辦法的時刻會立刻拋出來,WCF會捕捉這個異常並作為運用級其余異常停止正常處置;關於前者,經由過程下面的剖析我們曉得異常現實上產生在對前往“聚集對象”停止迭代的時刻。詳細是甚麼時刻呢?其實就是對前往對象停止序列化的時刻,此時拋出的異常將將會視為體系異常來處置。
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值得改良的處所,然則今朝來講為了不如許的成績,我推舉將WCF契約接口操作辦法中的前往類型界說成數組,而不是IEnumerable或許IEnumerable<T>(趁便說一下,WCF針對Array、List和其他聚集類型的序列化/反序列化行動是分歧的),然則我小我對IEnumerable或許IEnumerable<T>不排擠。