日韩天天综合网_野战两个奶头被亲到高潮_亚洲日韩欧美精品综合_av女人天堂污污污_视频一区**字幕无弹窗_国产亚洲欧美小视频_国内性爱精品在线免费视频_国产一级电影在线播放_日韩欧美内地福利_亚洲一二三不卡片区

解讀.NET中*延遲*特性的幾個(gè)陷阱_.Net教程

編輯Tag賺U幣
教程Tag:暫無(wú)Tag,歡迎添加,賺取U幣!

推薦:C#教程:Assembly類訪問(wèn)程序集信息
C#中通過(guò)Assembly類可以訪問(wèn)程序集信息. 1.允許訪問(wèn)給定程序集的元元素,包含可以加載和執(zhí)行程序集的方法; 2.加載程序集:使用靜態(tài)方法Assembly.Load(程序集名稱)或Assembly.LoadFrom(程序集完整路徑名); 3.屬性: FullName:程序集顯示名稱; 3.方法: Ge

.NET發(fā)展至今,其實(shí)各處都有“延遲(Lazy)”的痕跡,一個(gè)小小的“Laziness”給我們帶來(lái)了不少靈活性1。“延遲”的關(guān)鍵就在于“只在需要的時(shí)候處理數(shù)據(jù)”,老趙曾經(jīng)在多篇文章中提到了類似的概念,如《高階函數(shù)、委托與匿名方法》及《您善于使用匿名函數(shù)嗎?》。不過(guò)“延遲”本身也會(huì)給您帶來(lái)一些陷阱,某些陷阱您很有可能也曾經(jīng)遇到過(guò)。這篇文章便是總結(jié)了延遲特性的集中常見(jiàn)陷阱,并給出應(yīng)對(duì)方案。

重復(fù)運(yùn)算

問(wèn)題

“延遲”的本意是“減少計(jì)算”,但是如果您使用不當(dāng),很可能反而會(huì)造成“重復(fù)計(jì)算”。例如,我們首先構(gòu)建一個(gè)方法,它接受一個(gè)參數(shù)n,返回一個(gè)Func<int, bool>對(duì)象:

以下為引用的內(nèi)容:

static Func<int, bool> DivideBy(int n)
{
    return x =>
    {
        bool divisible = x % n == 0;
        Console.WriteLine(
            "{0} can be divisible by {1}? {2}",
            x, n, divisible ? "Yes" : "No");
        return divisible;
    };
}

返回的Func<int, bool>對(duì)象會(huì)根據(jù)傳入的參數(shù)x,返回一個(gè)表示x能否被n整除的布爾值。在這過(guò)程中,還會(huì)向控制臺(tái)輸出一句話,例如:“10 can be divisible by 3? No”。每當(dāng)看到這句話,則表明“經(jīng)過(guò)了一次判斷”。那么您是否知道,下面的代碼會(huì)輸出什么結(jié)果呢?

以下為引用的內(nèi)容:

List<int> values = new List<int>();
for (int i = 0; i < 10; i++) values.Add(i);

var divideByTwo = values.Where(DivideBy(2));
var divideByTwoAndThree = divideByTwo.Where(DivideBy(3));
var divideByTwoAndFive = divideByTwo.Where(DivideBy(5));

foreach (var i in divideByTwoAndThree) { }
foreach (var i in divideByTwoAndFive) { }

結(jié)果如下:

以下為引用的內(nèi)容:

0 can be divisible by 2? Yes
0 can be divisible by 3? Yes
1 can be divisible by 2? No
2 can be divisible by 2? Yes
2 can be divisible by 3? No
3 can be divisible by 2? No
4 can be divisible by 2? Yes
4 can be divisible by 3? No
5 can be divisible by 2? No
6 can be divisible by 2? Yes
6 can be divisible by 3? Yes
7 can be divisible by 2? No
8 can be divisible by 2? Yes
8 can be divisible by 3? No
9 can be divisible by 2? No
0 can be divisible by 2? Yes
0 can be divisible by 5? Yes
1 can be divisible by 2? No
2 can be divisible by 2? Yes
2 can be divisible by 5? No
3 can be divisible by 2? No
4 can be divisible by 2? Yes
4 can be divisible by 5? No
5 can be divisible by 2? No
6 can be divisible by 2? Yes
6 can be divisible by 5? No
7 can be divisible by 2? No
8 can be divisible by 2? Yes
8 can be divisible by 5? No
9 can be divisible by 2? No

您是否發(fā)現(xiàn),無(wú)論是在遍歷divideByTwoAndThree和divideByTwoAndFive序列時(shí),都會(huì)從原有的values序列里重新判斷每個(gè)元素是否能夠被2整除?這就是.NET 3.5中“Where”的延遲特性,如果您在這里沒(méi)有意識(shí)到這點(diǎn),就可能會(huì)產(chǎn)生重復(fù)計(jì)算,浪費(fèi)了計(jì)算能力。

解決方案

解決這個(gè)問(wèn)題的方法就是在合適的時(shí)候進(jìn)行“強(qiáng)制計(jì)算”。例如:

以下為引用的內(nèi)容:

var divideByTwo = values.Where(DivideBy(2)).ToList();
var divideByTwoAndThree = divideByTwo.Where(DivideBy(3));
var divideByTwoAndFive = divideByTwo.Where(DivideBy(5));

結(jié)果就變成了:

以下為引用的內(nèi)容:

0 can be divisible by 2? Yes
1 can be divisible by 2? No
2 can be divisible by 2? Yes
3 can be divisible by 2? No
4 can be divisible by 2? Yes
5 can be divisible by 2? No
6 can be divisible by 2? Yes
7 can be divisible by 2? No
8 can be divisible by 2? Yes
9 can be divisible by 2? No
0 can be divisible by 3? Yes
2 can be divisible by 3? No
4 can be divisible by 3? No
6 can be divisible by 3? Yes
8 can be divisible by 3? No
0 can be divisible by 5? Yes
2 can be divisible by 5? No
4 can be divisible by 5? No
6 can be divisible by 5? No
8 can be divisible by 5? No

此時(shí),在獲得divideByTwo序列時(shí),就會(huì)立即進(jìn)行計(jì)算,這樣在遍歷后兩者時(shí)就不會(huì)重復(fù)計(jì)算1,3,5等元素了。

異常陷阱

問(wèn)題

請(qǐng)問(wèn)您是否知道下面的代碼有什么問(wèn)題?

以下為引用的內(nèi)容:

public static IEnumerable<string> ToString(IEnumerable<int> source)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }

    foreach (int item in source)
    {
        yield return item.ToString();
    }
}

如果您沒(méi)有看出來(lái)的話,不如運(yùn)行一下這段代碼:

以下為引用的內(nèi)容:

static void Main(string[] args)
{
    IEnumerable<string> values;
    try
    {
        values = ToString(null);
    }
    catch (ArgumentNullException)
    {
        Console.WriteLine("Passed the null source");
        return;
    }

    foreach (var s in values) { }
}

請(qǐng)問(wèn),運(yùn)行上面的代碼是否會(huì)拋出異常?從代碼的意圖上看,在ToString方法的一開(kāi)始我們會(huì)檢查參數(shù)是否為null,然后拋出異常——這本應(yīng)被catch語(yǔ)句所捕獲。但是事實(shí)上,代碼直到foreach執(zhí)行時(shí)才真正拋出了異常。這種“延遲”執(zhí)行違反了我們的實(shí)現(xiàn)意圖。為什么會(huì)這樣呢?您可以使用.NET Reflector反編譯一下,查看一下yield語(yǔ)句的等價(jià)C#實(shí)現(xiàn)是什么樣的,一切就清楚了。

解決方案

對(duì)于這個(gè)問(wèn)題,一般我們可以使用一對(duì)public和private方法配合來(lái)使用:

以下為引用的內(nèi)容:

public static IEnumerable<string> ToString(IEnumerable<int> source)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }

    return ToStringInternal(source);
}

private static IEnumerable<string> ToStringInternal(IEnumerable<int> source)
{
    foreach (int item in source)
    {
        yield return item.ToString();
    }
}

不妨再去查看一下現(xiàn)在的C#代碼實(shí)現(xiàn)?

資源管理

問(wèn)題

由于是延遲執(zhí)行,一些原本最簡(jiǎn)單的代碼模式可能就破壞了。例如:

以下為引用的內(nèi)容:

static Func<string> ReadAllText(string file)
{
    using (Stream stream = File.OpenRead(file))
    {
        StreamReader reader = new StreamReader(stream);
        return reader.ReadToEnd;
    }
}

使用using來(lái)管理文件的打開(kāi)關(guān)閉是最容易不過(guò)的事情了,不過(guò)現(xiàn)在如果您通過(guò)ReadAllText(@"C:\abc.txt")方法獲得的Func<string>對(duì)象,在執(zhí)行時(shí)就會(huì)拋出ObjectDisposedException。這是因?yàn)樵疚覀円鈭D中的順序:

打開(kāi)文件

讀取內(nèi)容

關(guān)閉文件

因?yàn)橛?ldquo;延遲”特性,這個(gè)順序已經(jīng)變?yōu)椋?/p>

打開(kāi)文件

關(guān)閉文件

讀取內(nèi)容

這怎么能不出錯(cuò)?

解決方案

有朋友說(shuō),這個(gè)容易:

以下為引用的內(nèi)容:

static Func<string> ReadAllText(string file)
{
    using (Stream stream = File.OpenRead(file))
    {
        StreamReader reader = new StreamReader(stream);
        string text = reader.ReadToEnd();

        return () => text;
    }
}

的確沒(méi)有拋出異常了,但是這也喪失了“延遲”的特點(diǎn)了。我們必須讓它能夠在調(diào)用委托對(duì)象的時(shí)候,才去打開(kāi)文件:

以下為引用的內(nèi)容:

static Func<string> ReadAllText(string file)
{
    return () =>
    {
        using (Stream stream = File.OpenRead(file))
        {
            StreamReader reader = new StreamReader(stream);
            return reader.ReadToEnd();
        }
    };
}

值得一提的是,using完全可以配合yield語(yǔ)句使用。也就是說(shuō),您可以編寫這樣的代碼:

以下為引用的內(nèi)容:

static IEnumerable<string> AllLines(string file)
{
    using (Stream stream = File.OpenRead(file))
    {
        StreamReader reader = new StreamReader(stream);
        while (!reader.EndOfStream)
        {
            yield return reader.ReadLine();
        }
    }
}

由此也可見(jiàn)C#編譯器是多么的強(qiáng)大,它幫我們解決了非常重要的問(wèn)題。

閉包共享

問(wèn)題

其實(shí)這個(gè)問(wèn)題也已經(jīng)被談過(guò)很多次了,在這里提一下主要是為了保持內(nèi)容的完整性。您認(rèn)為,以下代碼結(jié)果如何?

以下為引用的內(nèi)容:

List<Action> actions = new List<Action>();
for (int i = 0; i < 10; i++)
{
    actions.Add(() => Console.WriteLine(i));
}

foreach (var a in actions) a();

它打印出來(lái)的結(jié)果是10個(gè)10,具體原因在《警惕匿名方法造成的變量共享》一文中已經(jīng)有過(guò)描述,概括而來(lái)便是:各個(gè)action共享一個(gè)閉包,導(dǎo)致其中的“i”并不是獨(dú)立的。

解決方案

解決這個(gè)問(wèn)題的方法,只需讓不同閉包訪問(wèn)的值相互獨(dú)立即可。如:

以下為引用的內(nèi)容:

List<Action> actions = new List<Action>();
for (int i = 0; i < 10; i++)
{
    int  j = i; // 新增代碼
    actions.Add(() => Console.WriteLine(j));
}

foreach (var a in actions) a();

關(guān)于“延遲”特性,您還有什么看法呢?

分享:解讀Entity Framework的默認(rèn)值BUG
前幾天常使用.Net 3.5里的Entity Framework做個(gè)網(wǎng)站的時(shí)候,發(fā)現(xiàn)了一個(gè)問(wèn)題:添加記錄時(shí),對(duì)于DateTime型的數(shù)據(jù),無(wú)法使用數(shù)據(jù)庫(kù)的默認(rèn)值。 雖然不是什么嚴(yán)重的問(wèn)題,但牛脾氣上來(lái)了 ,就行解決這個(gè)問(wèn)題。 具體的情況是這樣的,我的數(shù)據(jù)庫(kù)有個(gè)Users表,三個(gè)

來(lái)源:模板無(wú)憂//所屬分類:.Net教程/更新時(shí)間:2009-07-11
相關(guān).Net教程