欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于C#中yield?return用法的思考

 更新時間:2023年05月31日 09:31:32   作者:yi念之間  
在這篇文章中,我們將深入討論?C#?中yield?return的機制和用法,幫助您更好地理解這個強大的功能,并在實際開發(fā)中靈活使用它,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

前言

當(dāng)我們編寫 C# 代碼時,經(jīng)常需要處理大量的數(shù)據(jù)集合。在傳統(tǒng)的方式中,我們往往需要先將整個數(shù)據(jù)集合加載到內(nèi)存中,然后再進行操作。但是如果數(shù)據(jù)集合非常大,這種方式就會導(dǎo)致內(nèi)存占用過高,甚至可能導(dǎo)致程序崩潰。

C# 中的yield return機制可以幫助我們解決這個問題。通過使用yield return,我們可以將數(shù)據(jù)集合按需生成,而不是一次性生成整個數(shù)據(jù)集合。這樣可以大大減少內(nèi)存占用,并且提高程序的性能。

在本文中,我們將深入討論 C# 中yield return的機制和用法,幫助您更好地理解這個強大的功能,并在實際開發(fā)中靈活使用它。

使用方式

上面我們提到了yield return將數(shù)據(jù)集合按需生成,而不是一次性生成整個數(shù)據(jù)集合。接下來通過一個簡單的示例,我們看一下它的工作方式是什么樣的,以便加深對它的理解

foreach (var num in GetInts())
{
    Console.WriteLine("外部遍歷了:{0}", num);
}
IEnumerable<int> GetInts()
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("內(nèi)部遍歷了:{0}", i);
        yield return i;
    }
}

首先,在GetInts方法中,我們使用yield return關(guān)鍵字來定義一個迭代器。這個迭代器可以按需生成整數(shù)序列。在每次循環(huán)時,使用yield return返回當(dāng)前的整數(shù)。通過1foreach循環(huán)來遍歷 GetInts方法返回的整數(shù)序列。在迭代時GetInts方法會被執(zhí)行,但是不會將整個序列加載到內(nèi)存中。而是在需要時,按需生成序列中的每個元素。在每次迭代時,會輸出當(dāng)前迭代的整數(shù)對應(yīng)的信息。所以輸出的結(jié)果為

內(nèi)部遍歷了:0
外部遍歷了:0
內(nèi)部遍歷了:1
外部遍歷了:1
內(nèi)部遍歷了:2
外部遍歷了:2
內(nèi)部遍歷了:3
外部遍歷了:3
內(nèi)部遍歷了:4
外部遍歷了:4

可以看到,整數(shù)序列是按需生成的,并且在每次生成時都會輸出相應(yīng)的信息。這種方式可以大大減少內(nèi)存占用,并且提高程序的性能。當(dāng)然從c# 8開始異步迭代的方式同樣支持

await foreach (var num in GetIntsAsync())
{
    Console.WriteLine("外部遍歷了:{0}", num);
}
async IAsyncEnumerable<int> GetIntsAsync()
{
    for (int i = 0; i < 5; i++)
    {
        await Task.Yield();
        Console.WriteLine("內(nèi)部遍歷了:{0}", i);
        yield return i;
    }
}

和上面不同的是,如果需要用異步的方式,我們需要返回IAsyncEnumerable類型,這種方式的執(zhí)行結(jié)果和上面同步的方式執(zhí)行的結(jié)果是一致的,我們就不做展示了。上面我們的示例都是基于循環(huán)持續(xù)迭代的,其實使用yield return的方式還可以按需的方式去輸出,這種方式適合靈活迭代的方式。如下示例所示

foreach (var num in GetInts())
{
    Console.WriteLine("外部遍歷了:{0}", num);
}
IEnumerable<int> GetInts()
{
    Console.WriteLine("內(nèi)部遍歷了:0");
    yield return 0;
    Console.WriteLine("內(nèi)部遍歷了:1");
    yield return 1;
    Console.WriteLine("內(nèi)部遍歷了:2");
    yield return 2;
}

foreach循環(huán)每次會調(diào)用GetInts()方法,GetInts()方法的內(nèi)部便使用yield return關(guān)鍵字返回一個結(jié)果。每次遍歷都會去執(zhí)行下一個yield return。所以上面代碼輸出的結(jié)果是

內(nèi)部遍歷了:0
外部遍歷了:0
內(nèi)部遍歷了:1
外部遍歷了:1
內(nèi)部遍歷了:2
外部遍歷了:2

探究本質(zhì)

上面我們展示了yield return如何使用的示例,它是一種延遲加載的機制,它可以讓我們逐個地處理數(shù)據(jù),而不是一次性地將所有數(shù)據(jù)讀取到內(nèi)存中。接下來我們就來探究一下神奇操作的背后到底是如何實現(xiàn)的,方便讓大家更清晰的了解迭代體系相關(guān)。

foreach本質(zhì)

首先我們來看一下foreach為什么可以遍歷,也就是如果可以被foreach遍歷的對象,被遍歷的操作需要滿足哪些條件,這個時候我們可以反編譯工具來看一下編譯后的代碼是什么樣子的,相信大家最熟悉的就是List<T>集合的遍歷方式了,那我們就用List<T>的示例來演示一下

List<int> ints = new List<int>();
foreach(int item in ints)
{
    Console.WriteLine(item);
}

上面的這段代碼很簡單,我們也沒有給它任何初始化的數(shù)據(jù),這樣可以排除干擾,讓我們能更清晰的看到反編譯的結(jié)果,排除其他干擾。它反編譯后的代碼是這樣的

List<int> list = new List<int>();
List<int>.Enumerator enumerator = list.GetEnumerator();
try
{
    while (enumerator.MoveNext())
    {
        int current = enumerator.Current;
        Console.WriteLine(current);
    }
}
finally
{
    ((IDisposable)enumerator).Dispose();
}

可以反編譯代碼的工具有很多,我用的比較多的一般是ILSpydnSpy、dotPeek和在線c#反編譯網(wǎng)站sharplab.io,其中dnSpy還可以調(diào)試反編譯的代碼。

通過上面的反編譯之后的代碼我們可以看到foreach會被編譯成一個固定的結(jié)構(gòu),也就是我們經(jīng)常提及的設(shè)計模式中的迭代器模式結(jié)構(gòu)

Enumerator enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
   var current = enumerator.Current;
}

通過這段固定的結(jié)構(gòu)我們總結(jié)一下foreach的工作原理

  • 可以被foreach的對象需要要包含GetEnumerator()方法
  • 迭代器對象包含MoveNext()方法和Current屬性
  • MoveNext()方法返回bool類型,判斷是否可以繼續(xù)迭代。Current屬性返回當(dāng)前的迭代結(jié)果。

我們可以看一下List<T>類可迭代的源碼結(jié)構(gòu)是如何實現(xiàn)的

public class List<T> : IList<T>, IList, IReadOnlyList<T>
{
    public Enumerator GetEnumerator() => new Enumerator(this);
    IEnumerator<T> IEnumerable<T>.GetEnumerator() => Count == 0 ? SZGenericArrayEnumerator<T>.Empty : GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
    public struct Enumerator : IEnumerator<T>, IEnumerator
    {
        public T Current => _current!;
        public bool MoveNext()
        {
        }
    }
}

這里涉及到了兩個核心的接口IEnumerable<IEnumerator,他們兩個定義了可以實現(xiàn)迭代的能力抽象,實現(xiàn)方式如下

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}
public interface IEnumerator
{
    bool MoveNext();
    object Current{ get; }
    void Reset();
}

如果類實現(xiàn)IEnumerable接口并實現(xiàn)了GetEnumerator()方法便可以被foreach,迭代的對象是IEnumerator類型,包含一個MoveNext()方法和Current屬性。上面的接口是原始對象的方式,這種操作都是針對object類型集合對象。我們實際開發(fā)過程中大多數(shù)都是使用的泛型集合,當(dāng)然也有對應(yīng)的實現(xiàn)方式,如下所示

public interface IEnumerable<out T> : IEnumerable
{
    new IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
    new T Current{ get; }
}

可以被foreach迭代并不意味著一定要去實現(xiàn)IEnumerable接口,這只是給我們提供了一個可以被迭代的抽象的能力。只要類中包含GetEnumerator()方法并返回一個迭代器,迭代器里包含返回bool類型的MoveNext()方法和獲取當(dāng)前迭代對象的Current屬性即可。

yield return本質(zhì)

上面我們看到了可以被foreach迭代的本質(zhì)是什么,那么yield return的返回值可以被IEnumerable<T>接收說明其中必有蹊蹺,我們反編譯一下我們上面的示例看一下反編譯之后代碼,為了方便大家對比反編譯結(jié)果,這里我把上面的示例再次粘貼一下

foreach (var num in GetInts())
{
    Console.WriteLine("外部遍歷了:{0}", num);
}
IEnumerable<int> GetInts()
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("內(nèi)部遍歷了:{0}", i);
        yield return i;
    }
}

它的反編譯結(jié)果,這里咱們就不全部展示了,只展示一下核心的邏輯

//foeach編譯后的結(jié)果
IEnumerator<int> enumerator = GetInts().GetEnumerator();
try
{
    while (enumerator.MoveNext())
    {
        int current = enumerator.Current;
        Console.WriteLine("外部遍歷了:{0}", current);
    }
}
finally
{
    if (enumerator != null)
    {
        enumerator.Dispose();
    }
}
//GetInts方法編譯后的結(jié)果
private IEnumerable<int> GetInts()
{
    <GetInts>d__1 <GetInts>d__ = new <GetInts>d__1(-2);
    <GetInts>d__.<>4__this = this;
    return <GetInts>d__;
}

這里我們可以看到GetInts()方法里原來的代碼不見了,而是多了一個<GetInts>d__1l類型,也就是說yield return本質(zhì)是語法糖。我們看一下<GetInts>d__1類的實現(xiàn)

//生成的類即實現(xiàn)了IEnumerable接口也實現(xiàn)了IEnumerator接口
//說明它既包含了GetEnumerator()方法,也包含MoveNext()方法和Current屬性
private sealed class <>GetIntsd__1 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
{
    private int <>1__state;
    //當(dāng)前迭代結(jié)果
    private int <>2__current;
    private int <>l__initialThreadId;
    public C <>4__this;
    private int <i>5__1;
    //當(dāng)前迭代到的結(jié)果
    int IEnumerator<int>.Current
    {
        get{ return <>2__current; }
    }
    //當(dāng)前迭代到的結(jié)果
    object IEnumerator.Current
    {
        get{ return <>2__current; }
    }
    //構(gòu)造函數(shù)包含狀態(tài)字段,變向說明靠狀態(tài)機去實現(xiàn)核心流程流轉(zhuǎn)
    public <GetInts>d__1(int <>1__state)
    {
        this.<>1__state = <>1__state;
        <>l__initialThreadId = Environment.CurrentManagedThreadId;
    }
    //核心方法MoveNext
    private bool MoveNext()
    {
        int num = <>1__state;
        if (num != 0)
        {
            if (num != 1)
            {
                return false;
            }
            //控制狀態(tài)
            <>1__state = -1;
            //自增 也就是代碼里循環(huán)的i++
            <i>5__1++;
        }
        else
        {
            <>1__state = -1;
            <i>5__1 = 0;
        }
        //循環(huán)終止條件 上面循環(huán)里的i<5
        if (<i>5__1 < 5)
        {
            Console.WriteLine("內(nèi)部遍歷了:{0}", <i>5__1);
            //把當(dāng)前迭代結(jié)果賦值給Current屬性
            <>2__current = <i>5__1;
            <>1__state = 1;
            //說明可以繼續(xù)迭代
            return true;
        }
        //迭代結(jié)束
        return false;
    }
    //IEnumerator的MoveNext方法
    bool IEnumerator.MoveNext()
    {
        return this.MoveNext();
    }
    //IEnumerable的IEnumerable方法
    IEnumerator<int> IEnumerable<int>.IEnumerable()
    {
        //實例化<GetInts>d__1實例
        <GetInts>d__1 <GetInts>d__;
        if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
        {
            <>1__state = 0;
            <GetInts>d__ = this;
        }
        else
        {
            //給狀態(tài)機初始化
            <GetInts>d__ = new <GetInts>d__1(0);
            <GetInts>d__.<>4__this = <>4__this;
        }
        //因為<GetInts>d__1實現(xiàn)了IEnumerator接口所以可以直接返回
        return <GetInts>d__;
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        //因為<GetInts>d__1實現(xiàn)了IEnumerator接口所以可以直接轉(zhuǎn)換
        return ((IEnumerable<int>)this).GetEnumerator();
    }
    void IEnumerator.Reset()
    {
    }
    void IDisposable.Dispose()
    {
    }
}

通過它生成的類我們可以看到,該類即實現(xiàn)了IEnumerable接口也實現(xiàn)了IEnumerator接口說明它既包含了GetEnumerator()方法,也包含MoveNext()方法和Current屬性。用這一個類就可以滿足可被foeach迭代的核心結(jié)構(gòu)。我們手動寫的for代碼被包含到了MoveNext()方法里,它包含了定義的狀態(tài)機制代碼,并且根據(jù)當(dāng)前的狀態(tài)機代碼將迭代移動到下一個元素。我們大概講解一下我們的for代碼被翻譯到MoveNext()方法里的執(zhí)行流程

  • 首次迭代時<>1__state被初始化成0,代表首個被迭代的元素,這個時候Current初始值為0,循環(huán)控制變量<i>5__1初始值也為0。
  • 判斷是否滿足終止條件,不滿足則執(zhí)行循環(huán)里的邏輯。并更改裝填機<>1__state為1,代表首次迭代執(zhí)行完成。
  • 循環(huán)控制變量<i>5__1繼續(xù)自增并更改并更改裝填機<>1__state為-1,代表可持續(xù)迭代。并循環(huán)執(zhí)行循環(huán)體的自定義邏輯。
  • 不滿足迭代條件則返回false,也就是代表了MoveNext()以不滿足迭代條件while (enumerator.MoveNext())邏輯終止。

上面我們還展示了另一種yield return的方式,就是同一個方法里包含多個yield return的形式

IEnumerable<int> GetInts()
{
    Console.WriteLine("內(nèi)部遍歷了:0");
    yield return 0;
    Console.WriteLine("內(nèi)部遍歷了:1");
    yield return 1;
    Console.WriteLine("內(nèi)部遍歷了:2");
    yield return 2;
}

上面這段代碼反編譯的結(jié)果如下所示,這里咱們只展示核心的方法MoveNext()的實現(xiàn)

private bool MoveNext()
{
    switch (<>1__state)
    {
        default:
            return false;
        case 0:
            <>1__state = -1;
            Console.WriteLine("內(nèi)部遍歷了:0");
            <>2__current = 0;
            <>1__state = 1;
            return true;
        case 1:
            <>1__state = -1;
            Console.WriteLine("內(nèi)部遍歷了:1");
            <>2__current = 1;
            <>1__state = 2;
            return true;
        case 2:
            <>1__state = -1;
            Console.WriteLine("內(nèi)部遍歷了:2");
            <>2__current = 2;
            <>1__state = 3;
            return true;
        case 3:
            <>1__state = -1;
            return false;
    }
}

通過編譯后的代碼我們可以看到,多個yield return的形式會被編譯成switch...case的形式,有幾個yield return則會編譯成n+1case,多出來的一個case則代表的MoveNext()終止條件,也就是返回false的條件。其它的case則返回true表示可以繼續(xù)迭代。

IAsyncEnumerable接口

上面我們展示了同步yield return方式,c# 8開始新增了IAsyncEnumerable<T>接口,用于完成異步迭代,也就是迭代器邏輯里包含異步邏輯的場景。IAsyncEnumerable<T>接口的實現(xiàn)代碼如下所示

public interface IAsyncEnumerable<out T>
{
    IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
public interface IAsyncEnumerator<out T> : IAsyncDisposable
{
    ValueTask<bool> MoveNextAsync();
    T Current { get; }
}

它最大的不同則是同步的IEnumerator包含的是MoveNext()方法返回的是bool,IAsyncEnumerator接口包含的是MoveNextAsync()異步方法,返回的是ValueTask<bool>類型。所以上面的示例代碼

await foreach (var num in GetIntsAsync())
{
    Console.WriteLine("外部遍歷了:{0}", num);
}

所以這里的await雖然是加在foreach上面,但是實際作用的則是每一次迭代執(zhí)行的MoveNextAsync()方法??梢源笾吕斫鉃橄旅娴墓ぷ鞣绞?/p>

IAsyncEnumerator<int> enumerator = list.GetAsyncEnumerator();
while (enumerator.MoveNextAsync().GetAwaiter().GetResult())
{
   var current = enumerator.Current;
}

當(dāng)然,實際編譯成的代碼并不是這個樣子的,我們在之前的文章<研究c#異步操作async await狀態(tài)機的總結(jié)>一文中講解過async await會被編譯成IAsyncStateMachine異步狀態(tài)機,所以IAsyncEnumerator<T>結(jié)合yield return的實現(xiàn)比同步的方式更加復(fù)雜而且包含更多的代碼,不過實現(xiàn)原理可以結(jié)合同步的方式類比一下,但是要同時了解異步狀態(tài)機的實現(xiàn),這里咱們就不過多展示異步y(tǒng)ield return的編譯后實現(xiàn)了,有興趣的同學(xué)可以自行了解一下。

foreach增強

c# 9增加了對foreach的增強的功能,即通過擴展方法的形式,對原本具備包含foreach能力的對象增加GetEnumerator()方法,使得普通類在不具備foreach的能力的情況下也可以使用來迭代。它的使用方式如下

Foo foo = new Foo();
foreach (int item in foo)
{
    Console.WriteLine(item);
}
public class Foo
{
    public List<int> Ints { get; set; } = new List<int>();
}
public static class Bar
{
    //給Foo定義擴展方法
    public static IEnumerator<int> GetEnumerator(this Foo foo)
    {
        foreach (int item in foo.Ints)
        {
            yield return item;
        }
    }
}

這個功能確實比較強大,滿足開放封閉原則,我們可以在不修改原始代碼的情況,增強代碼的功能,可以說是非常的實用。我們來看一下它的編譯后的結(jié)果是啥

Foo foo = new Foo();
IEnumerator<int> enumerator = Bar.GetEnumerator(foo);
try
{
    while (enumerator.MoveNext())
    {
        int current = enumerator.Current;
        Console.WriteLine(current);
    }
}
finally
{
    if (enumerator != null)
    {
        enumerator.Dispose();
    }
}

這里我們看到擴展方法GetEnumerator()本質(zhì)也是語法糖,會把擴展能力編譯成擴展類.GetEnumerator(被擴展實例)的方式。也就是我們寫代碼時候的原始方式,只是編譯器幫我們生成了它的調(diào)用方式。接下來我們看一下GetEnumerator()擴展方法編譯成了什么

public static IEnumerator<int> GetEnumerator(Foo foo)
{
    <GetEnumerator>d__0 <GetEnumerator>d__ = new <GetEnumerator>d__0(0);
    <GetEnumerator>d__.foo = foo;
    return <GetEnumerator>d__;
}

看到這個代碼是不是覺得很眼熟了,不錯和上面yield return本質(zhì)這一節(jié)里講到的語法糖生成方式是一樣的了,同樣的編譯時候也是生成了一個對應(yīng)類,這里的類是<GetEnumerator>d__0,我們看一下該類的結(jié)構(gòu)

private sealed class <GetEnumerator>d__0 : IEnumerator<int>, IEnumerator, IDisposable
{
    private int <>1__state;
    private int <>2__current;
    public Foo foo;
    private List<int>.Enumerator <>s__1;
    private int <item>5__2;
    int IEnumerator<int>.Current
    {
        get{ return <>2__current; }
    }
    object IEnumerator.Current
    {
        get{ return <>2__current; }
    }
    public <GetEnumerator>d__0(int <>1__state)
    {
        this.<>1__state = <>1__state;
    }
    private bool MoveNext()
    {
        try
        {
            int num = <>1__state;
            if (num != 0)
            {
                if (num != 1)
                {
                    return false;
                }
                <>1__state = -3;
            }
            else
            {
                <>1__state = -1;
                //因為示例中的Ints我們使用的是List<T>
                <>s__1 = foo.Ints.GetEnumerator();
                <>1__state = -3;
            }
            //因為上面的擴展方法里使用的是foreach遍歷方式
            //這里也被編譯成了實際生產(chǎn)方式
            if (<>s__1.MoveNext())
            {
                <item>5__2 = <>s__1.Current;
                <>2__current = <item>5__2;
                <>1__state = 1;
                return true;
            }
            <>m__Finally1();
            <>s__1 = default(List<int>.Enumerator);
            return false;
        }
        catch
        {
            ((IDisposable)this).Dispose();
            throw;
        }
    }
    bool IEnumerator.MoveNext()
    {
        return this.MoveNext();
    }
    void IDisposable.Dispose()
    {
    }
    void IEnumerator.Reset()
    {
    }
    private void <>m__Finally1()
    {
    }
}

看到編譯器生成的代碼,我們可以看到yield return生成的代碼結(jié)構(gòu)都是一樣的,只是MoveNext()里的邏輯取決于我們寫代碼時候的具體邏輯,不同的邏輯生成不同的代碼。這里咱們就不在講解它生成的代碼了,因為和上面咱們講解的代碼邏輯是差不多的。

總結(jié)

通過本文我們介紹了c#中的yield return語法,并探討了由它帶來的一些思考。我們通過一些簡單的例子,展示了yield return的使用方式,知道了迭代器來是如何按需處理大量數(shù)據(jù)。同時,我們通過分析foreach迭代和yield return語法的本質(zhì),講解了它們的實現(xiàn)原理和底層機制。好在涉及到的知識整體比較簡單,仔細(xì)閱讀相關(guān)實現(xiàn)代碼的話相信會了解背后的實現(xiàn)原理,這里就不過多贅述了。

到此這篇關(guān)于關(guān)于C#中yield return用法的思考的文章就介紹到這了,更多相關(guān)C# yield return內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#實現(xiàn)Post數(shù)據(jù)或文件到指定的服務(wù)器進行接收

    C#實現(xiàn)Post數(shù)據(jù)或文件到指定的服務(wù)器進行接收

    這篇文章主要為大家詳細(xì)介紹了如何通過C#實現(xiàn)Post數(shù)據(jù)或文件到指定的服務(wù)器進行接收,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考下
    2024-03-03
  • C#使用委托的形式調(diào)用線程代碼實例

    C#使用委托的形式調(diào)用線程代碼實例

    今天小編就為大家分享一篇關(guān)于C#使用委托的形式調(diào)用線程代碼實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • C#?CefSharp?根據(jù)輸入日期段自動選擇日期的操作代碼

    C#?CefSharp?根據(jù)輸入日期段自動選擇日期的操作代碼

    這篇文章主要介紹了C#?CefSharp?根據(jù)輸入日期段自動選擇日期的操作代碼,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • 測試框架nunit之a(chǎn)ssertion斷言使用詳解

    測試框架nunit之a(chǎn)ssertion斷言使用詳解

    NUnit是.Net平臺的測試框架,廣泛用于.Net平臺的單元測試和回歸測試中,下面我們用示例詳細(xì)學(xué)習(xí)一下他的使用方法
    2014-01-01
  • 仿orm自動生成分頁SQL分享

    仿orm自動生成分頁SQL分享

    平時接觸的數(shù)據(jù)庫有sql2000-2008,Oracle,SQLite 。 分頁邏輯,Oracle和SQLite相對好寫,就SQL事多,Sql2000下只能用top,排序2次,而Sql2005+就可以使用ROW_NUMBER()分析函數(shù)了,據(jù)說Sql2012對分頁又有了改進
    2014-01-01
  • C#入門之窗體的簡單用法實例

    C#入門之窗體的簡單用法實例

    這篇文章主要介紹了C#入門之窗體的簡單用法,以實例形式分析了注冊頁面程序的實現(xiàn)過程,具有一定的參考借鑒價值,需要的朋友可以參考下
    2014-12-12
  • C#中sealed修飾符的具體使用

    C#中sealed修飾符的具體使用

    在 C# 中,sealed?是一個修飾符,用于限制繼承和重寫,本文就來介紹一下具體使用,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • unity實現(xiàn)簡單的貪吃蛇游戲

    unity實現(xiàn)簡單的貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了unity實現(xiàn)簡單的貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • C#動態(tài)編譯并執(zhí)行字符串樣例

    C#動態(tài)編譯并執(zhí)行字符串樣例

    這篇文章主要為大家詳細(xì)介紹了C#動態(tài)編譯并執(zhí)行字符串樣例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • C#中DataGridView的樣式

    C#中DataGridView的樣式

    這篇文章主要介紹了C#中DataGridView的樣式設(shè)置方法,包括交替行顏色、單元格內(nèi)容有效性檢查、單元格的選擇模式等,需要的朋友可以參考下
    2015-02-02

最新評論