一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

c#對于如何釋放資源的解釋

 kittywei 2015-03-31
 

當(dāng)我們使用非托管資源(unmanaged resources)類型時,應(yīng)當(dāng)使用IDisposable接口的Dispose()方法來釋放資源。在.Net環(huán)境中,對非托管資源的回收不是系統(tǒng)的責(zé)任,我們必須自己調(diào)用Dispose()方法來釋放資源。確保非托管資源會釋放的最好方法是使用using或者try/finally。


      所有的非托管資源類型都實現(xiàn)了IDisposable接口。另外當(dāng)我們沒有明確的釋放資源,比如我們忘記了,C#還會防護(hù)性的通過創(chuàng)建終結(jié)器(finalizer)來釋放資源。如果我們希望應(yīng)用程序運行的更快時,就應(yīng)當(dāng)盡快釋放一些不必要的資源。幸運的是在C#中有新的關(guān)鍵字來完成這項任務(wù)。我們先考慮下面的代碼:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            myConnection.Open();

            myCommand.ExecuteNonQuery();

        }

      有兩個對象沒有被釋放掉:SqlConnection和SqlCommand。它們都會保存在內(nèi)存中直到終結(jié)器被調(diào)用為止。


      通過下面的修改,我們可以釋放它們:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            myConnection.Open();

            myCommand.ExecuteNonQuery();


            myCommand.Dispose();

            myConnection.Dispose();

        }

 


      這樣做是正確的,但前提是SqlCommand沒有拋出異常。一旦出現(xiàn)異常,我們的Dispose()方法就不會運行了。using關(guān)鍵字可以幫助我們確保Dispose()會被運行。當(dāng)我們使用using的時候,C#的編譯器會將它轉(zhuǎn)換成為類似與try/finally的形式:


        public void ExcuteCommand(string connectString, string commandString)

        {

            using(SqlConnection myConnection = new SqlConnection(connectString))

            {

                using(SqlCommand myCommand = new SqlCommand(commandString, myConnection))

                {

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

            }

        }

 


      下例中的兩段代碼會生成非常相似的IL


using(SqlConnection myConnection = new SqlConnection(connectString))

{

      myConnection.Open();

}


try

{

      SqlConnection myConnection = new SqlConnection(connectString);

      myConnection.Open();

}

finally

{

      myConnection.Dispose();

}

 


      當(dāng)我們使用非托管資源時,使用using是確保資源合理釋放的簡單途徑。如果我們對不支持IDisposable接口的類型使用using關(guān)鍵字,編譯器會報錯:


//錯誤

using(string msg = "this is a message")

{

      Console.WriteLine(msg);

}

      另外using只檢驗編譯時類型是否支持IDisposable接口,它不能識別運行時的對象。下例中即便Factory.CreateResource()返回的類型支持IDisposable接口也是不能通過編譯的:


//錯誤

using(object obj = Factory.CreateResource)

{

}

 


      對于可能支持可能不支持IDisposable接口的對象,我們可以這樣來處理:


object obj = Factory.CreateResource();

using(obj as IDisposable)

{

}

 


      如果對象實現(xiàn)了IDisposable接口,就可以生成釋放資源的代碼。如果不支持,則生成using(null),雖然不做任何工作,但也是安全的。如果我們拿不準(zhǔn)是否應(yīng)該將對象放在using中,那么比較穩(wěn)妥的做法是將它放進(jìn)去。


      當(dāng)我們在程序中使用了非托管資源類型時,我們應(yīng)當(dāng)將其放入using的括號中。當(dāng)有多個需要釋放的資源,例如前面的例子中的connection和command,我們應(yīng)當(dāng)創(chuàng)建多個using,每一個包含一個對應(yīng)的對象。這些using會被轉(zhuǎn)化為不同的try/finally塊,在效果上看就好像是下面這段代碼:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = null;

            SqlCommand myCommand = null;

            try

            {

                myConnection = new SqlConnection(connectString);

                try

                {

                    myCommand = new SqlCommand(commandString, myConnection);

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

                finally

                {

                    if(myCommand != null)

                    {

                        myCommand.Dispose();

                    }

                }

            }

            finally

            {

                if(myConnection != null)

                {

                    myConnection.Dispose();

                }

            }

        }

      每個using聲明都創(chuàng)建了一個try/finally程序塊。我們自己也可以通過這樣寫來取消多層嵌套:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = null;

            SqlCommand myCommand = null;

            try

            {

                myConnection = new SqlConnection(connectString);

                myCommand = new SqlCommand(commandString, myConnection);


                myConnection.Open();

                myCommand.ExecuteNonQuery();

            }

            finally

            {

                if(myCommand != null)

                {

                    myCommand.Dispose();

                }

                if(myConnection != null)

                {

                    myConnection.Dispose();

                }

            }

        }

 


      雖然看起來很簡潔,但是我們不要這樣使用using聲明:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            using(myConnection as IDisposable)

            {

                using(myCommand as IDisposable)

                {

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

            }

        }

 


      這樣做是有潛在bug的。一旦SqlCommand的構(gòu)造函數(shù)拋出異常,SqlConnection就無法被釋放了。我們必須保證每個非托管資源對象都可以被順利的釋放,否則可能會造成內(nèi)存資源的浪費。對于單個的非托管資源對象,使用using關(guān)鍵字是最好的方法。對于多個對象,我們可以使用嵌套using或者自己寫try/finally的方法來釋放資源。


      在釋放資源上還有一個細(xì)節(jié),有些類型不僅有Dispose()方法,還有Close()方法,例如SqlConnection。我們這樣可以關(guān)閉SqlConnection的連接:


myConnection.Close();

      這樣做的確能夠釋放連接,但是并不是和Dispose()方法一樣。Dispose()方法除了釋放資源之外,還有其他的工作:它會通知垃圾收集器(Garbage Collector)這個對象的資源已經(jīng)被釋放了,而不必在終結(jié)器中進(jìn)行重復(fù)的操作。Dispose()調(diào)用了GC.SuppressFinalize()方法,這個方法請求系統(tǒng)不要調(diào)用指定對象的終結(jié)器。而Close()不是這樣的。使用Close()釋放的對象雖然已經(jīng)不必調(diào)用終結(jié)器,但是它還是存在于終結(jié)器的釋放資源隊列當(dāng)中。Dispose()比Close()的工作做的更加徹底。


      Dispose()并沒有將對象移出內(nèi)存。它為對象添加了一個釋放資源的鉤子(hook)。這就意味著我們可以對正在使用的對象使用Dispose(),我們應(yīng)當(dāng)小心這一點。


      在C#中大部分的類型都不支持Dispose()。在超過1500種類型中只有100來種實現(xiàn)了IDispose接口。當(dāng)我們使用實現(xiàn)了這個接口的對象時,我們應(yīng)當(dāng)在適當(dāng)?shù)臅r候使用using或try/finally塊的方法來釋放資源。


      譯自   Effective C#:50 Specific Ways to Improve Your C#                     
Bill Wagner著


      回到目錄


P.S. 以下為個人想法:


      Dispose()和Close()都是可以顯示調(diào)用來釋放資源。而Finalize()是受保護(hù)的,析構(gòu)函數(shù)更是不知道什么時候會被執(zhí)行。被Close()掉的對象是有可能再次復(fù)活的,比如SqlConnection,還可以通過Open()繼續(xù)使用,好像是休眠狀態(tài)。而被Dispose()掉的對象從理論上來說是不應(yīng)當(dāng)再復(fù)活了,因為我們在Dispose的同時應(yīng)當(dāng)告訴GC這個對象已經(jīng)over了,以避免重復(fù)的對象清除工作。當(dāng)然我們可以通過釋放資源時獲得其引用的詭異方法來繼續(xù)訪問對象,但是由于Dispose()調(diào)用GC.SuppressFinalize()方法免除終結(jié),因此這些Dispose()掉又復(fù)活的對象的資源就再也不會自動被GC釋放了。


    public class DisposeClass : IDisposable

    {

        private string insideResources;


        public void UseInsideResources()

        {

            insideResources = "use resouces";

            Console.WriteLine(insideResources);

        }


        public DisposeClass()

        {

            Console.WriteLine("create object");

        }


        ~DisposeClass()

        {

            Console.WriteLine("destroy~ object");

        }


        IDisposable 成員#region IDisposable 成員


        public void Dispose()

        {

            // TODO:  添加 DisposeClass.Dispose 實現(xiàn)

            Console.WriteLine("dispose object");

            GC.SuppressFinalize( this );

        }


        #endregion

    }

      上例中的類在使用Dispose()釋放資源后是不會調(diào)用析構(gòu)函數(shù)的,沒有調(diào)用Dispose()時才會在需要終結(jié)時調(diào)用析構(gòu)函數(shù)。但是如果我們這樣使用它:


    static void Main(string[] args)

    {

        ArrayList myList = new ArrayList();

        using(DisposeClass a = new DisposeClass())

        {

            myList.Add(a);

        }

        ((DisposeClass)(myList[0])).UseInsideResources();

        Console.ReadLine();

    }

      已經(jīng)被Dispose掉的對象又復(fù)活了。而且析構(gòu)函數(shù)不會再被調(diào)用。我們同樣可以在析構(gòu)函數(shù)中做這種事,只是我們不知道它什么時候會發(fā)生而已。


      Finalize()是一個神奇的函數(shù),在沒有析構(gòu)函數(shù)時它可以像諸如abc()一樣做為普通的成員函數(shù),但是一旦析構(gòu)函數(shù)存在,就會報出“已經(jīng)存在名為Finalize()成員”的錯誤。更匪夷所思的是當(dāng)我們將其聲明為保護(hù)成員函數(shù)(開始時我將其聲明為public的,不會出現(xiàn)特殊的結(jié)果,后來才改成protected)時,它就會被其派生類的析構(gòu)函數(shù)調(diào)用。在C#中,派生類的析構(gòu)函數(shù)會自動調(diào)用基類的析構(gòu)函數(shù),因此在這里派生類看來已經(jīng)把它當(dāng)作是基類的析構(gòu)函數(shù)了。在編譯器中應(yīng)該將它同析構(gòu)函數(shù)劃了等號了吧,可是object.Finalize()明明是不能重寫的。對于protected
void Finalize()來說,它到底算是個什么東西呢?這應(yīng)該是在編譯階段就可以發(fā)現(xiàn)的錯誤吧?代碼如下:


    static void Main(string[] args)

    {

        ExtendDisposeClass a = new ExtendDisposeClass();

        Console.ReadLine();

    }


    public class DisposeClass : IDisposable

    {

        public DisposeClass()

        {

            Console.WriteLine("create object");

        }


//        ~DisposeClass()

//        {

//            Console.WriteLine("destroy~ object");

//        }


        protected void Finalize()

        {

            Console.WriteLine("finalize object");

        }


        IDisposable 成員#region IDisposable 成員


        public void Dispose()

        {

            // TODO:  添加 DisposeClass.Dispose 實現(xiàn)

            Console.WriteLine("dispose object");

        }


        #endregion

    }


    public class ExtendDisposeClass : DisposeClass

    {

        ~ExtendDisposeClass()

        {

            Console.WriteLine("destroyExtendDisposeClass object");

        }

    }


 

當(dāng)我們使用非托管資源(unmanaged resources)類型時,應(yīng)當(dāng)使用IDisposable接口的Dispose()方法來釋放資源。在.Net環(huán)境中,對非托管資源的回收不是系統(tǒng)的責(zé)任,我們必須自己調(diào)用Dispose()方法來釋放資源。確保非托管資源會釋放的最好方法是使用using或者try/finally。


      所有的非托管資源類型都實現(xiàn)了IDisposable接口。另外當(dāng)我們沒有明確的釋放資源,比如我們忘記了,C#還會防護(hù)性的通過創(chuàng)建終結(jié)器(finalizer)來釋放資源。如果我們希望應(yīng)用程序運行的更快時,就應(yīng)當(dāng)盡快釋放一些不必要的資源。幸運的是在C#中有新的關(guān)鍵字來完成這項任務(wù)。我們先考慮下面的代碼:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            myConnection.Open();

            myCommand.ExecuteNonQuery();

        }

      有兩個對象沒有被釋放掉:SqlConnection和SqlCommand。它們都會保存在內(nèi)存中直到終結(jié)器被調(diào)用為止。


      通過下面的修改,我們可以釋放它們:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            myConnection.Open();

            myCommand.ExecuteNonQuery();


            myCommand.Dispose();

            myConnection.Dispose();

        }

 


      這樣做是正確的,但前提是SqlCommand沒有拋出異常。一旦出現(xiàn)異常,我們的Dispose()方法就不會運行了。using關(guān)鍵字可以幫助我們確保Dispose()會被運行。當(dāng)我們使用using的時候,C#的編譯器會將它轉(zhuǎn)換成為類似與try/finally的形式:


        public void ExcuteCommand(string connectString, string commandString)

        {

            using(SqlConnection myConnection = new SqlConnection(connectString))

            {

                using(SqlCommand myCommand = new SqlCommand(commandString, myConnection))

                {

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

            }

        }

 


      下例中的兩段代碼會生成非常相似的IL


using(SqlConnection myConnection = new SqlConnection(connectString))

{

      myConnection.Open();

}


try

{

      SqlConnection myConnection = new SqlConnection(connectString);

      myConnection.Open();

}

finally

{

      myConnection.Dispose();

}

 


      當(dāng)我們使用非托管資源時,使用using是確保資源合理釋放的簡單途徑。如果我們對不支持IDisposable接口的類型使用using關(guān)鍵字,編譯器會報錯:


//錯誤

using(string msg = "this is a message")

{

      Console.WriteLine(msg);

}

      另外using只檢驗編譯時類型是否支持IDisposable接口,它不能識別運行時的對象。下例中即便Factory.CreateResource()返回的類型支持IDisposable接口也是不能通過編譯的:


//錯誤

using(object obj = Factory.CreateResource)

{

}

 


      對于可能支持可能不支持IDisposable接口的對象,我們可以這樣來處理:


object obj = Factory.CreateResource();

using(obj as IDisposable)

{

}

 


      如果對象實現(xiàn)了IDisposable接口,就可以生成釋放資源的代碼。如果不支持,則生成using(null),雖然不做任何工作,但也是安全的。如果我們拿不準(zhǔn)是否應(yīng)該將對象放在using中,那么比較穩(wěn)妥的做法是將它放進(jìn)去。


      當(dāng)我們在程序中使用了非托管資源類型時,我們應(yīng)當(dāng)將其放入using的括號中。當(dāng)有多個需要釋放的資源,例如前面的例子中的connection和command,我們應(yīng)當(dāng)創(chuàng)建多個using,每一個包含一個對應(yīng)的對象。這些using會被轉(zhuǎn)化為不同的try/finally塊,在效果上看就好像是下面這段代碼:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = null;

            SqlCommand myCommand = null;

            try

            {

                myConnection = new SqlConnection(connectString);

                try

                {

                    myCommand = new SqlCommand(commandString, myConnection);

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

                finally

                {

                    if(myCommand != null)

                    {

                        myCommand.Dispose();

                    }

                }

            }

            finally

            {

                if(myConnection != null)

                {

                    myConnection.Dispose();

                }

            }

        }

      每個using聲明都創(chuàng)建了一個try/finally程序塊。我們自己也可以通過這樣寫來取消多層嵌套:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = null;

            SqlCommand myCommand = null;

            try

            {

                myConnection = new SqlConnection(connectString);

                myCommand = new SqlCommand(commandString, myConnection);


                myConnection.Open();

                myCommand.ExecuteNonQuery();

            }

            finally

            {

                if(myCommand != null)

                {

                    myCommand.Dispose();

                }

                if(myConnection != null)

                {

                    myConnection.Dispose();

                }

            }

        }

 


      雖然看起來很簡潔,但是我們不要這樣使用using聲明:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            using(myConnection as IDisposable)

            {

                using(myCommand as IDisposable)

                {

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

            }

        }

 


      這樣做是有潛在bug的。一旦SqlCommand的構(gòu)造函數(shù)拋出異常,SqlConnection就無法被釋放了。我們必須保證每個非托管資源對象都可以被順利的釋放,否則可能會造成內(nèi)存資源的浪費。對于單個的非托管資源對象,使用using關(guān)鍵字是最好的方法。對于多個對象,我們可以使用嵌套using或者自己寫try/finally的方法來釋放資源。


      在釋放資源上還有一個細(xì)節(jié),有些類型不僅有Dispose()方法,還有Close()方法,例如SqlConnection。我們這樣可以關(guān)閉SqlConnection的連接:


myConnection.Close();

      這樣做的確能夠釋放連接,但是并不是和Dispose()方法一樣。Dispose()方法除了釋放資源之外,還有其他的工作:它會通知垃圾收集器(Garbage Collector)這個對象的資源已經(jīng)被釋放了,而不必在終結(jié)器中進(jìn)行重復(fù)的操作。Dispose()調(diào)用了GC.SuppressFinalize()方法,這個方法請求系統(tǒng)不要調(diào)用指定對象的終結(jié)器。而Close()不是這樣的。使用Close()釋放的對象雖然已經(jīng)不必調(diào)用終結(jié)器,但是它還是存在于終結(jié)器的釋放資源隊列當(dāng)中。Dispose()比Close()的工作做的更加徹底。


      Dispose()并沒有將對象移出內(nèi)存。它為對象添加了一個釋放資源的鉤子(hook)。這就意味著我們可以對正在使用的對象使用Dispose(),我們應(yīng)當(dāng)小心這一點。


      在C#中大部分的類型都不支持Dispose()。在超過1500種類型中只有100來種實現(xiàn)了IDispose接口。當(dāng)我們使用實現(xiàn)了這個接口的對象時,我們應(yīng)當(dāng)在適當(dāng)?shù)臅r候使用using或try/finally塊的方法來釋放資源。


      譯自   Effective C#:50 Specific Ways to Improve Your C#                     
Bill Wagner著


      回到目錄


P.S. 以下為個人想法:


      Dispose()和Close()都是可以顯示調(diào)用來釋放資源。而Finalize()是受保護(hù)的,析構(gòu)函數(shù)更是不知道什么時候會被執(zhí)行。被Close()掉的對象是有可能再次復(fù)活的,比如SqlConnection,還可以通過Open()繼續(xù)使用,好像是休眠狀態(tài)。而被Dispose()掉的對象從理論上來說是不應(yīng)當(dāng)再復(fù)活了,因為我們在Dispose的同時應(yīng)當(dāng)告訴GC這個對象已經(jīng)over了,以避免重復(fù)的對象清除工作。當(dāng)然我們可以通過釋放資源時獲得其引用的詭異方法來繼續(xù)訪問對象,但是由于Dispose()調(diào)用GC.SuppressFinalize()方法免除終結(jié),因此這些Dispose()掉又復(fù)活的對象的資源就再也不會自動被GC釋放了。


    public class DisposeClass : IDisposable

    {

        private string insideResources;


        public void UseInsideResources()

        {

            insideResources = "use resouces";

            Console.WriteLine(insideResources);

        }


        public DisposeClass()

        {

            Console.WriteLine("create object");

        }


        ~DisposeClass()

        {

            Console.WriteLine("destroy~ object");

        }


        IDisposable 成員#region IDisposable 成員


        public void Dispose()

        {

            // TODO:  添加 DisposeClass.Dispose 實現(xiàn)

            Console.WriteLine("dispose object");

            GC.SuppressFinalize( this );

        }


        #endregion

    }

      上例中的類在使用Dispose()釋放資源后是不會調(diào)用析構(gòu)函數(shù)的,沒有調(diào)用Dispose()時才會在需要終結(jié)時調(diào)用析構(gòu)函數(shù)。但是如果我們這樣使用它:


    static void Main(string[] args)

    {

        ArrayList myList = new ArrayList();

        using(DisposeClass a = new DisposeClass())

        {

            myList.Add(a);

        }

        ((DisposeClass)(myList[0])).UseInsideResources();

        Console.ReadLine();

    }

      已經(jīng)被Dispose掉的對象又復(fù)活了。而且析構(gòu)函數(shù)不會再被調(diào)用。我們同樣可以在析構(gòu)函數(shù)中做這種事,只是我們不知道它什么時候會發(fā)生而已。


      Finalize()是一個神奇的函數(shù),在沒有析構(gòu)函數(shù)時它可以像諸如abc()一樣做為普通的成員函數(shù),但是一旦析構(gòu)函數(shù)存在,就會報出“已經(jīng)存在名為Finalize()成員”的錯誤。更匪夷所思的是當(dāng)我們將其聲明為保護(hù)成員函數(shù)(開始時我將其聲明為public的,不會出現(xiàn)特殊的結(jié)果,后來才改成protected)時,它就會被其派生類的析構(gòu)函數(shù)調(diào)用。在C#中,派生類的析構(gòu)函數(shù)會自動調(diào)用基類的析構(gòu)函數(shù),因此在這里派生類看來已經(jīng)把它當(dāng)作是基類的析構(gòu)函數(shù)了。在編譯器中應(yīng)該將它同析構(gòu)函數(shù)劃了等號了吧,可是object.Finalize()明明是不能重寫的。對于protected
void Finalize()來說,它到底算是個什么東西呢?這應(yīng)該是在編譯階段就可以發(fā)現(xiàn)的錯誤吧?代碼如下:


    static void Main(string[] args)

    {

        ExtendDisposeClass a = new ExtendDisposeClass();

        Console.ReadLine();

    }


    public class DisposeClass : IDisposable

    {

        public DisposeClass()

        {

            Console.WriteLine("create object");

        }


//        ~DisposeClass()

//        {

//            Console.WriteLine("destroy~ object");

//        }


        protected void Finalize()

        {

            Console.WriteLine("finalize object");

        }


        IDisposable 成員#region IDisposable 成員


        public void Dispose()

        {

            // TODO:  添加 DisposeClass.Dispose 實現(xiàn)

            Console.WriteLine("dispose object");

        }


        #endregion

    }


    public class ExtendDisposeClass : DisposeClass

    {

        ~ExtendDisposeClass()

        {

            Console.WriteLine("destroyExtendDisposeClass object");

        }

    }


 

當(dāng)我們使用非托管資源(unmanaged resources)類型時,應(yīng)當(dāng)使用IDisposable接口的Dispose()方法來釋放資源。在.Net環(huán)境中,對非托管資源的回收不是系統(tǒng)的責(zé)任,我們必須自己調(diào)用Dispose()方法來釋放資源。確保非托管資源會釋放的最好方法是使用using或者try/finally。


      所有的非托管資源類型都實現(xiàn)了IDisposable接口。另外當(dāng)我們沒有明確的釋放資源,比如我們忘記了,C#還會防護(hù)性的通過創(chuàng)建終結(jié)器(finalizer)來釋放資源。如果我們希望應(yīng)用程序運行的更快時,就應(yīng)當(dāng)盡快釋放一些不必要的資源。幸運的是在C#中有新的關(guān)鍵字來完成這項任務(wù)。我們先考慮下面的代碼:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            myConnection.Open();

            myCommand.ExecuteNonQuery();

        }

      有兩個對象沒有被釋放掉:SqlConnection和SqlCommand。它們都會保存在內(nèi)存中直到終結(jié)器被調(diào)用為止。


      通過下面的修改,我們可以釋放它們:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            myConnection.Open();

            myCommand.ExecuteNonQuery();


            myCommand.Dispose();

            myConnection.Dispose();

        }

 


      這樣做是正確的,但前提是SqlCommand沒有拋出異常。一旦出現(xiàn)異常,我們的Dispose()方法就不會運行了。using關(guān)鍵字可以幫助我們確保Dispose()會被運行。當(dāng)我們使用using的時候,C#的編譯器會將它轉(zhuǎn)換成為類似與try/finally的形式:


        public void ExcuteCommand(string connectString, string commandString)

        {

            using(SqlConnection myConnection = new SqlConnection(connectString))

            {

                using(SqlCommand myCommand = new SqlCommand(commandString, myConnection))

                {

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

            }

        }

 


      下例中的兩段代碼會生成非常相似的IL


using(SqlConnection myConnection = new SqlConnection(connectString))

{

      myConnection.Open();

}


try

{

      SqlConnection myConnection = new SqlConnection(connectString);

      myConnection.Open();

}

finally

{

      myConnection.Dispose();

}

 


      當(dāng)我們使用非托管資源時,使用using是確保資源合理釋放的簡單途徑。如果我們對不支持IDisposable接口的類型使用using關(guān)鍵字,編譯器會報錯:


//錯誤

using(string msg = "this is a message")

{

      Console.WriteLine(msg);

}

      另外using只檢驗編譯時類型是否支持IDisposable接口,它不能識別運行時的對象。下例中即便Factory.CreateResource()返回的類型支持IDisposable接口也是不能通過編譯的:


//錯誤

using(object obj = Factory.CreateResource)

{

}

 


      對于可能支持可能不支持IDisposable接口的對象,我們可以這樣來處理:


object obj = Factory.CreateResource();

using(obj as IDisposable)

{

}

 


      如果對象實現(xiàn)了IDisposable接口,就可以生成釋放資源的代碼。如果不支持,則生成using(null),雖然不做任何工作,但也是安全的。如果我們拿不準(zhǔn)是否應(yīng)該將對象放在using中,那么比較穩(wěn)妥的做法是將它放進(jìn)去。


      當(dāng)我們在程序中使用了非托管資源類型時,我們應(yīng)當(dāng)將其放入using的括號中。當(dāng)有多個需要釋放的資源,例如前面的例子中的connection和command,我們應(yīng)當(dāng)創(chuàng)建多個using,每一個包含一個對應(yīng)的對象。這些using會被轉(zhuǎn)化為不同的try/finally塊,在效果上看就好像是下面這段代碼:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = null;

            SqlCommand myCommand = null;

            try

            {

                myConnection = new SqlConnection(connectString);

                try

                {

                    myCommand = new SqlCommand(commandString, myConnection);

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

                finally

                {

                    if(myCommand != null)

                    {

                        myCommand.Dispose();

                    }

                }

            }

            finally

            {

                if(myConnection != null)

                {

                    myConnection.Dispose();

                }

            }

        }

      每個using聲明都創(chuàng)建了一個try/finally程序塊。我們自己也可以通過這樣寫來取消多層嵌套:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = null;

            SqlCommand myCommand = null;

            try

            {

                myConnection = new SqlConnection(connectString);

                myCommand = new SqlCommand(commandString, myConnection);


                myConnection.Open();

                myCommand.ExecuteNonQuery();

            }

            finally

            {

                if(myCommand != null)

                {

                    myCommand.Dispose();

                }

                if(myConnection != null)

                {

                    myConnection.Dispose();

                }

            }

        }

 


      雖然看起來很簡潔,但是我們不要這樣使用using聲明:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            using(myConnection as IDisposable)

            {

                using(myCommand as IDisposable)

                {

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

            }

        }

 


      這樣做是有潛在bug的。一旦SqlCommand的構(gòu)造函數(shù)拋出異常,SqlConnection就無法被釋放了。我們必須保證每個非托管資源對象都可以被順利的釋放,否則可能會造成內(nèi)存資源的浪費。對于單個的非托管資源對象,使用using關(guān)鍵字是最好的方法。對于多個對象,我們可以使用嵌套using或者自己寫try/finally的方法來釋放資源。


      在釋放資源上還有一個細(xì)節(jié),有些類型不僅有Dispose()方法,還有Close()方法,例如SqlConnection。我們這樣可以關(guān)閉SqlConnection的連接:


myConnection.Close();

      這樣做的確能夠釋放連接,但是并不是和Dispose()方法一樣。Dispose()方法除了釋放資源之外,還有其他的工作:它會通知垃圾收集器(Garbage Collector)這個對象的資源已經(jīng)被釋放了,而不必在終結(jié)器中進(jìn)行重復(fù)的操作。Dispose()調(diào)用了GC.SuppressFinalize()方法,這個方法請求系統(tǒng)不要調(diào)用指定對象的終結(jié)器。而Close()不是這樣的。使用Close()釋放的對象雖然已經(jīng)不必調(diào)用終結(jié)器,但是它還是存在于終結(jié)器的釋放資源隊列當(dāng)中。Dispose()比Close()的工作做的更加徹底。


      Dispose()并沒有將對象移出內(nèi)存。它為對象添加了一個釋放資源的鉤子(hook)。這就意味著我們可以對正在使用的對象使用Dispose(),我們應(yīng)當(dāng)小心這一點。


      在C#中大部分的類型都不支持Dispose()。在超過1500種類型中只有100來種實現(xiàn)了IDispose接口。當(dāng)我們使用實現(xiàn)了這個接口的對象時,我們應(yīng)當(dāng)在適當(dāng)?shù)臅r候使用using或try/finally塊的方法來釋放資源。


      譯自   Effective C#:50 Specific Ways to Improve Your C#                     
Bill Wagner著


      回到目錄


P.S. 以下為個人想法:


      Dispose()和Close()都是可以顯示調(diào)用來釋放資源。而Finalize()是受保護(hù)的,析構(gòu)函數(shù)更是不知道什么時候會被執(zhí)行。被Close()掉的對象是有可能再次復(fù)活的,比如SqlConnection,還可以通過Open()繼續(xù)使用,好像是休眠狀態(tài)。而被Dispose()掉的對象從理論上來說是不應(yīng)當(dāng)再復(fù)活了,因為我們在Dispose的同時應(yīng)當(dāng)告訴GC這個對象已經(jīng)over了,以避免重復(fù)的對象清除工作。當(dāng)然我們可以通過釋放資源時獲得其引用的詭異方法來繼續(xù)訪問對象,但是由于Dispose()調(diào)用GC.SuppressFinalize()方法免除終結(jié),因此這些Dispose()掉又復(fù)活的對象的資源就再也不會自動被GC釋放了。


    public class DisposeClass : IDisposable

    {

        private string insideResources;


        public void UseInsideResources()

        {

            insideResources = "use resouces";

            Console.WriteLine(insideResources);

        }


        public DisposeClass()

        {

            Console.WriteLine("create object");

        }


        ~DisposeClass()

        {

            Console.WriteLine("destroy~ object");

        }


        IDisposable 成員#region IDisposable 成員


        public void Dispose()

        {

            // TODO:  添加 DisposeClass.Dispose 實現(xiàn)

            Console.WriteLine("dispose object");

            GC.SuppressFinalize( this );

        }


        #endregion

    }

      上例中的類在使用Dispose()釋放資源后是不會調(diào)用析構(gòu)函數(shù)的,沒有調(diào)用Dispose()時才會在需要終結(jié)時調(diào)用析構(gòu)函數(shù)。但是如果我們這樣使用它:


    static void Main(string[] args)

    {

        ArrayList myList = new ArrayList();

        using(DisposeClass a = new DisposeClass())

        {

            myList.Add(a);

        }

        ((DisposeClass)(myList[0])).UseInsideResources();

        Console.ReadLine();

    }

      已經(jīng)被Dispose掉的對象又復(fù)活了。而且析構(gòu)函數(shù)不會再被調(diào)用。我們同樣可以在析構(gòu)函數(shù)中做這種事,只是我們不知道它什么時候會發(fā)生而已。


      Finalize()是一個神奇的函數(shù),在沒有析構(gòu)函數(shù)時它可以像諸如abc()一樣做為普通的成員函數(shù),但是一旦析構(gòu)函數(shù)存在,就會報出“已經(jīng)存在名為Finalize()成員”的錯誤。更匪夷所思的是當(dāng)我們將其聲明為保護(hù)成員函數(shù)(開始時我將其聲明為public的,不會出現(xiàn)特殊的結(jié)果,后來才改成protected)時,它就會被其派生類的析構(gòu)函數(shù)調(diào)用。在C#中,派生類的析構(gòu)函數(shù)會自動調(diào)用基類的析構(gòu)函數(shù),因此在這里派生類看來已經(jīng)把它當(dāng)作是基類的析構(gòu)函數(shù)了。在編譯器中應(yīng)該將它同析構(gòu)函數(shù)劃了等號了吧,可是object.Finalize()明明是不能重寫的。對于protected
void Finalize()來說,它到底算是個什么東西呢?這應(yīng)該是在編譯階段就可以發(fā)現(xiàn)的錯誤吧?代碼如下:


    static void Main(string[] args)

    {

        ExtendDisposeClass a = new ExtendDisposeClass();

        Console.ReadLine();

    }


    public class DisposeClass : IDisposable

    {

        public DisposeClass()

        {

            Console.WriteLine("create object");

        }


//        ~DisposeClass()

//        {

//            Console.WriteLine("destroy~ object");

//        }


        protected void Finalize()

        {

            Console.WriteLine("finalize object");

        }


        IDisposable 成員#region IDisposable 成員


        public void Dispose()

        {

            // TODO:  添加 DisposeClass.Dispose 實現(xiàn)

            Console.WriteLine("dispose object");

        }


        #endregion

    }


    public class ExtendDisposeClass : DisposeClass

    {

        ~ExtendDisposeClass()

        {

            Console.WriteLine("destroyExtendDisposeClass object");

        }

    }


 

當(dāng)我們使用非托管資源(unmanaged resources)類型時,應(yīng)當(dāng)使用IDisposable接口的Dispose()方法來釋放資源。在.Net環(huán)境中,對非托管資源的回收不是系統(tǒng)的責(zé)任,我們必須自己調(diào)用Dispose()方法來釋放資源。確保非托管資源會釋放的最好方法是使用using或者try/finally。


      所有的非托管資源類型都實現(xiàn)了IDisposable接口。另外當(dāng)我們沒有明確的釋放資源,比如我們忘記了,C#還會防護(hù)性的通過創(chuàng)建終結(jié)器(finalizer)來釋放資源。如果我們希望應(yīng)用程序運行的更快時,就應(yīng)當(dāng)盡快釋放一些不必要的資源。幸運的是在C#中有新的關(guān)鍵字來完成這項任務(wù)。我們先考慮下面的代碼:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            myConnection.Open();

            myCommand.ExecuteNonQuery();

        }

      有兩個對象沒有被釋放掉:SqlConnection和SqlCommand。它們都會保存在內(nèi)存中直到終結(jié)器被調(diào)用為止。


      通過下面的修改,我們可以釋放它們:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            myConnection.Open();

            myCommand.ExecuteNonQuery();


            myCommand.Dispose();

            myConnection.Dispose();

        }

 


      這樣做是正確的,但前提是SqlCommand沒有拋出異常。一旦出現(xiàn)異常,我們的Dispose()方法就不會運行了。using關(guān)鍵字可以幫助我們確保Dispose()會被運行。當(dāng)我們使用using的時候,C#的編譯器會將它轉(zhuǎn)換成為類似與try/finally的形式:


        public void ExcuteCommand(string connectString, string commandString)

        {

            using(SqlConnection myConnection = new SqlConnection(connectString))

            {

                using(SqlCommand myCommand = new SqlCommand(commandString, myConnection))

                {

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

            }

        }

 


      下例中的兩段代碼會生成非常相似的IL


using(SqlConnection myConnection = new SqlConnection(connectString))

{

      myConnection.Open();

}


try

{

      SqlConnection myConnection = new SqlConnection(connectString);

      myConnection.Open();

}

finally

{

      myConnection.Dispose();

}

 


      當(dāng)我們使用非托管資源時,使用using是確保資源合理釋放的簡單途徑。如果我們對不支持IDisposable接口的類型使用using關(guān)鍵字,編譯器會報錯:


//錯誤

using(string msg = "this is a message")

{

      Console.WriteLine(msg);

}

      另外using只檢驗編譯時類型是否支持IDisposable接口,它不能識別運行時的對象。下例中即便Factory.CreateResource()返回的類型支持IDisposable接口也是不能通過編譯的:


//錯誤

using(object obj = Factory.CreateResource)

{

}

 


      對于可能支持可能不支持IDisposable接口的對象,我們可以這樣來處理:


object obj = Factory.CreateResource();

using(obj as IDisposable)

{

}

 


      如果對象實現(xiàn)了IDisposable接口,就可以生成釋放資源的代碼。如果不支持,則生成using(null),雖然不做任何工作,但也是安全的。如果我們拿不準(zhǔn)是否應(yīng)該將對象放在using中,那么比較穩(wěn)妥的做法是將它放進(jìn)去。


      當(dāng)我們在程序中使用了非托管資源類型時,我們應(yīng)當(dāng)將其放入using的括號中。當(dāng)有多個需要釋放的資源,例如前面的例子中的connection和command,我們應(yīng)當(dāng)創(chuàng)建多個using,每一個包含一個對應(yīng)的對象。這些using會被轉(zhuǎn)化為不同的try/finally塊,在效果上看就好像是下面這段代碼:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = null;

            SqlCommand myCommand = null;

            try

            {

                myConnection = new SqlConnection(connectString);

                try

                {

                    myCommand = new SqlCommand(commandString, myConnection);

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

                finally

                {

                    if(myCommand != null)

                    {

                        myCommand.Dispose();

                    }

                }

            }

            finally

            {

                if(myConnection != null)

                {

                    myConnection.Dispose();

                }

            }

        }

      每個using聲明都創(chuàng)建了一個try/finally程序塊。我們自己也可以通過這樣寫來取消多層嵌套:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = null;

            SqlCommand myCommand = null;

            try

            {

                myConnection = new SqlConnection(connectString);

                myCommand = new SqlCommand(commandString, myConnection);


                myConnection.Open();

                myCommand.ExecuteNonQuery();

            }

            finally

            {

                if(myCommand != null)

                {

                    myCommand.Dispose();

                }

                if(myConnection != null)

                {

                    myConnection.Dispose();

                }

            }

        }

 


      雖然看起來很簡潔,但是我們不要這樣使用using聲明:


        public void ExcuteCommand(string connectString, string commandString)

        {

            SqlConnection myConnection = new SqlConnection(connectString);

            SqlCommand myCommand = new SqlCommand(commandString, myConnection);

            using(myConnection as IDisposable)

            {

                using(myCommand as IDisposable)

                {

                    myConnection.Open();

                    myCommand.ExecuteNonQuery();

                }

            }

        }

 


      這樣做是有潛在bug的。一旦SqlCommand的構(gòu)造函數(shù)拋出異常,SqlConnection就無法被釋放了。我們必須保證每個非托管資源對象都可以被順利的釋放,否則可能會造成內(nèi)存資源的浪費。對于單個的非托管資源對象,使用using關(guān)鍵字是最好的方法。對于多個對象,我們可以使用嵌套using或者自己寫try/finally的方法來釋放資源。


      在釋放資源上還有一個細(xì)節(jié),有些類型不僅有Dispose()方法,還有Close()方法,例如SqlConnection。我們這樣可以關(guān)閉SqlConnection的連接:


myConnection.Close();

      這樣做的確能夠釋放連接,但是并不是和Dispose()方法一樣。Dispose()方法除了釋放資源之外,還有其他的工作:它會通知垃圾收集器(Garbage Collector)這個對象的資源已經(jīng)被釋放了,而不必在終結(jié)器中進(jìn)行重復(fù)的操作。Dispose()調(diào)用了GC.SuppressFinalize()方法,這個方法請求系統(tǒng)不要調(diào)用指定對象的終結(jié)器。而Close()不是這樣的。使用Close()釋放的對象雖然已經(jīng)不必調(diào)用終結(jié)器,但是它還是存在于終結(jié)器的釋放資源隊列當(dāng)中。Dispose()比Close()的工作做的更加徹底。


      Dispose()并沒有將對象移出內(nèi)存。它為對象添加了一個釋放資源的鉤子(hook)。這就意味著我們可以對正在使用的對象使用Dispose(),我們應(yīng)當(dāng)小心這一點。


      在C#中大部分的類型都不支持Dispose()。在超過1500種類型中只有100來種實現(xiàn)了IDispose接口。當(dāng)我們使用實現(xiàn)了這個接口的對象時,我們應(yīng)當(dāng)在適當(dāng)?shù)臅r候使用using或try/finally塊的方法來釋放資源。


      譯自   Effective C#:50 Specific Ways to Improve Your C#                     
Bill Wagner著


      回到目錄


P.S. 以下為個人想法:


      Dispose()和Close()都是可以顯示調(diào)用來釋放資源。而Finalize()是受保護(hù)的,析構(gòu)函數(shù)更是不知道什么時候會被執(zhí)行。被Close()掉的對象是有可能再次復(fù)活的,比如SqlConnection,還可以通過Open()繼續(xù)使用,好像是休眠狀態(tài)。而被Dispose()掉的對象從理論上來說是不應(yīng)當(dāng)再復(fù)活了,因為我們在Dispose的同時應(yīng)當(dāng)告訴GC這個對象已經(jīng)over了,以避免重復(fù)的對象清除工作。當(dāng)然我們可以通過釋放資源時獲得其引用的詭異方法來繼續(xù)訪問對象,但是由于Dispose()調(diào)用GC.SuppressFinalize()方法免除終結(jié),因此這些Dispose()掉又復(fù)活的對象的資源就再也不會自動被GC釋放了。


    public class DisposeClass : IDisposable

    {

        private string insideResources;


        public void UseInsideResources()

        {

            insideResources = "use resouces";

            Console.WriteLine(insideResources);

        }


        public DisposeClass()

        {

            Console.WriteLine("create object");

        }


        ~DisposeClass()

        {

            Console.WriteLine("destroy~ object");

        }


        IDisposable 成員#region IDisposable 成員


        public void Dispose()

        {

            // TODO:  添加 DisposeClass.Dispose 實現(xiàn)

            Console.WriteLine("dispose object");

            GC.SuppressFinalize( this );

        }


        #endregion

    }

      上例中的類在使用Dispose()釋放資源后是不會調(diào)用析構(gòu)函數(shù)的,沒有調(diào)用Dispose()時才會在需要終結(jié)時調(diào)用析構(gòu)函數(shù)。但是如果我們這樣使用它:


    static void Main(string[] args)

    {

        ArrayList myList = new ArrayList();

        using(DisposeClass a = new DisposeClass())

        {

            myList.Add(a);

        }

        ((DisposeClass)(myList[0])).UseInsideResources();

        Console.ReadLine();

    }

      已經(jīng)被Dispose掉的對象又復(fù)活了。而且析構(gòu)函數(shù)不會再被調(diào)用。我們同樣可以在析構(gòu)函數(shù)中做這種事,只是我們不知道它什么時候會發(fā)生而已。


      Finalize()是一個神奇的函數(shù),在沒有析構(gòu)函數(shù)時它可以像諸如abc()一樣做為普通的成員函數(shù),但是一旦析構(gòu)函數(shù)存在,就會報出“已經(jīng)存在名為Finalize()成員”的錯誤。更匪夷所思的是當(dāng)我們將其聲明為保護(hù)成員函數(shù)(開始時我將其聲明為public的,不會出現(xiàn)特殊的結(jié)果,后來才改成protected)時,它就會被其派生類的析構(gòu)函數(shù)調(diào)用。在C#中,派生類的析構(gòu)函數(shù)會自動調(diào)用基類的析構(gòu)函數(shù),因此在這里派生類看來已經(jīng)把它當(dāng)作是基類的析構(gòu)函數(shù)了。在編譯器中應(yīng)該將它同析構(gòu)函數(shù)劃了等號了吧,可是object.Finalize()明明是不能重寫的。對于protected
void Finalize()來說,它到底算是個什么東西呢?這應(yīng)該是在編譯階段就可以發(fā)現(xiàn)的錯誤吧?代碼如下:


    static void Main(string[] args)

    {

        ExtendDisposeClass a = new ExtendDisposeClass();

        Console.ReadLine();

    }


    public class DisposeClass : IDisposable

    {

        public DisposeClass()

        {

            Console.WriteLine("create object");

        }


//        ~DisposeClass()

//        {

//            Console.WriteLine("destroy~ object");

//        }


        protected void Finalize()

        {

            Console.WriteLine("finalize object");

        }


        IDisposable 成員#region IDisposable 成員


        public void Dispose()

        {

            // TODO:  添加 DisposeClass.Dispose 實現(xiàn)

            Console.WriteLine("dispose object");

        }


        #endregion

    }


    public class ExtendDisposeClass : DisposeClass

    {

        ~ExtendDisposeClass()

        {

            Console.WriteLine("destroyExtendDisposeClass object");

        }

    }


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    色鬼综合久久鬼色88| 日韩三级黄色大片免费观看 | 日本一区二区三区黄色| 欧美黑人在线一区二区| 91久久精品国产一区蜜臀| 亚洲一区二区精品免费视频| 欧美成人高清在线播放| 国产欧美一区二区色综合| 亚洲av在线视频一区| 日韩精品综合福利在线观看| 日本午夜乱色视频在线观看| 欧美夫妻性生活一区二区| 高清国产日韩欧美熟女| 国产又黄又爽又粗视频在线| 日韩国产中文在线视频| 国产精品一区欧美二区| 欧美日韩中国性生活视频 | 日韩在线免费看中文字幕| 99国产精品国产精品九九 | 欧美三级大黄片免费看| 日本东京热加勒比一区二区| 国产欧美另类激情久久久| 99免费人成看国产片| 久久99爱爱视频视频| 日本午夜免费啪视频在线| 国产熟女一区二区三区四区| 日本精品视频一二三区| 青草草在线视频免费视频| 激情五月天免费在线观看| 国产内射一级一片内射高清| 精品人妻一区二区四区| 国产欧美另类激情久久久| 成人精品一区二区三区在线| 日韩特级黄片免费在线观看| 中文字幕人妻综合一区二区| 99国产一区在线播放| 国产一级性生活录像片| 久久国产精品热爱视频| 欧美人妻盗摄日韩偷拍| 91人妻久久精品一区二区三区 | 久久re6热在线视频|