亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解C#如何計(jì)算一個(gè)實(shí)例占用多少內(nèi)存

 更新時(shí)間:2023年06月05日 09:26:05   作者:Artech  
我們都知道CPU和內(nèi)存是程序最為重要的兩類指標(biāo),那么有多少人真正想過(guò)一個(gè)類型的實(shí)例在內(nèi)存中究竟占多少字節(jié),本文就來(lái)用C#計(jì)算一下一個(gè)實(shí)例占用多少內(nèi)存吧

我們都知道CPU和內(nèi)存是程序最為重要的兩類指標(biāo),那么有多少人真正想過(guò)這個(gè)問(wèn)題:一個(gè)類型(值類型或者引用類型)的實(shí)例在內(nèi)存中究竟占多少字節(jié)?我們很多人都回答不上來(lái)。其實(shí)C#提供了一些用于計(jì)算大小的操作符和API,但是它們都不能完全解決我剛才提出的問(wèn)題。本文提供了一種計(jì)算值類型和引用類型實(shí)例所占內(nèi)存字節(jié)數(shù)量的方法。

一、sizeof操作符

sizeof操作用來(lái)確定某個(gè)類型對(duì)應(yīng)實(shí)例所占用的字節(jié)數(shù),但是它只能應(yīng)用在Unmanaged類型上。所謂的Unmanaged類型僅限于:

  • 原生類型(Primitive Type:Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, 和Single)
  • Decimal類型
  • 枚舉類型
  • 指針類型
  • 只包含Unmanaged類型數(shù)據(jù)成員的結(jié)構(gòu)體

顧名思義,一個(gè)Unmanaged類型是一個(gè)值類型,對(duì)應(yīng)的實(shí)例不能包含任何一個(gè)針對(duì)托管對(duì)象的引用。如果我們定義如下這樣一個(gè)泛型方法來(lái)調(diào)用sizeof操作符,泛型參數(shù)T必須添加unmananged約束,而且方法上還得添加unsafe標(biāo)記。

public static unsafe int SizeOf<T>() where T : unmanaged => sizeof(T);

只有原生類型和枚舉類型可以直接使用sizeof操作符,如果將它應(yīng)用在其他類型(指針和自定義結(jié)構(gòu)體),必須添加/unsafe編譯標(biāo)記,還需要放在unsafe上下文中。

Debug.Assert(sizeof(byte) == 1);
Debug.Assert(sizeof(sbyte) == 1);
Debug.Assert(sizeof(short) == 2);
Debug.Assert(sizeof(ushort) == 2);
Debug.Assert(sizeof(int) == 4);
Debug.Assert(sizeof(uint) == 4);
Debug.Assert(sizeof(long) == 8);
Debug.Assert(sizeof(ulong) == 8);
Debug.Assert(sizeof(char) == 2);
Debug.Assert(sizeof(float) == 4);
Debug.Assert(sizeof(double) == 8);
Debug.Assert(sizeof(bool) == 1);
Debug.Assert(sizeof(decimal) == 16);
Debug.Assert(sizeof(DateTimeKind) == 4);
unsafe
{
    Debug.Assert(sizeof(int*) == 8);
    Debug.Assert(sizeof(DateTime) == 8);
    Debug.Assert(sizeof(DateTimeOffset) == 16);
    Debug.Assert(sizeof(Guid) == 16);
    Debug.Assert(sizeof(Point) == 8);
}

由于如下這個(gè)結(jié)構(gòu)體Foobar并不是一個(gè)Unmanaged類型,所以程序會(huì)出現(xiàn)編譯錯(cuò)誤。

unsafe
{
    Debug.Assert(sizeof(Foobar) == 16);
}
public struct Foobar
{
    public string Foo;
    public int Bar;
}

二、Marshal.SizeOf方法

靜態(tài)類型Marshal定義了一系列API用來(lái)幫助我們完成非托管內(nèi)存的分配與拷貝、托管類型和非托管類型之間的轉(zhuǎn)換,以及其他一系列非托管內(nèi)存的操作(Marshal在計(jì)算科學(xué)中表示為了數(shù)據(jù)存儲(chǔ)或者傳輸而將內(nèi)存對(duì)象轉(zhuǎn)換成相應(yīng)的格式的操作)。靜態(tài)其中就包括如下4個(gè)SizeOf方法重載來(lái)確定指定類型或者對(duì)象的字節(jié)數(shù)。

public static class Marshal
{
    public static int SizeOf(object structure);
    public static int SizeOf<T>(T structure);
    public static int SizeOf(Type t);
    public static int SizeOf<T>()
}

Marshal.SizeOf方法雖然對(duì)指定的類型沒(méi)有針對(duì)Unmanaged類型的限制,但是依然要求指定一個(gè)值類型。如果傳入的是一個(gè)對(duì)象,該對(duì)象也必須是對(duì)一個(gè)值類型的裝箱。

object  value = default(Foobar);
Debug.Assert(Marshal.SizeOf<Foobar>() == 16);
Debug.Assert(Marshal.SizeOf(value) == 16);
Debug.Assert(Marshal.SizeOf(typeof(Foobar)) == 16);
Debug.Assert(Marshal.SizeOf(typeof(Foobar)) == 16);
public struct Foobar
{
    public object Foo;
    public object Bar;
}

由于如下這個(gè)Foobar被定義成了類,所以針對(duì)兩個(gè)SizeOf方法的調(diào)用都會(huì)拋出ArgumentException異常,并提示:Type 'Foobar' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.

Marshal.SizeOf<Foobar>();
Marshal.SizeOf(new Foobar());
public class Foobar
{
    public object Foo;
    public object Bar;
}

Marshal.SizeOf方法不支持泛型,還對(duì)結(jié)構(gòu)體的布局有要求,它支持支Sequential和Explicit布局模式。由于如下所示的Foobar結(jié)構(gòu)體采用Auto布局模式(由于非托管環(huán)境具有更加嚴(yán)格的內(nèi)存布局要求,所以不支持Auto這種根據(jù)字段成員對(duì)內(nèi)存布局進(jìn)行“動(dòng)態(tài)規(guī)劃”的方式),所以針對(duì)SizeOf方法的調(diào)用還是會(huì)拋出和上面一樣的ArgumentException異常。

Marshal.SizeOf<Foobar>();
[StructLayout(LayoutKind.Auto)]
public struct Foobar
{
    public int Foo;
    public int Bar;
}

三、Unsafe.SizeOf方法>

靜態(tài)Unsafe提供了針對(duì)非托管內(nèi)存更加底層的操作,類似的SizeIOf方法同樣定義在該類型中。該方法對(duì)指定的類型沒(méi)有任何限制,但是如果你指定的是引用類型,它會(huì)返回“指針字節(jié)數(shù)”(IntPtr.Size)。

public static class Unsafe
{
    public static int SizeOf<T>();
}
Debug.Assert( Unsafe.SizeOf<FoobarStructure>() == 16);
Debug.Assert( Unsafe.SizeOf<FoobarClass>() == 8);
public struct FoobarStructure
{
    public long Foo;
    public long Bar;
}
public class FoobarClass
{
    public long Foo;
    public long Bar;
}

四、可以根據(jù)字段成員的類型來(lái)計(jì)算嗎

我們知道不論是值類型還是引用類型,對(duì)應(yīng)的實(shí)例都映射為一段連續(xù)的片段(或者直接存儲(chǔ)在寄存器)。類型的目的就在于規(guī)定了對(duì)象的內(nèi)存布局,具有相同類型的實(shí)例具有相同的布局,字節(jié)數(shù)量自然相同(對(duì)于引用類型的字段,它在這段字節(jié)序列中只存儲(chǔ)引用的地址)。既然字節(jié)長(zhǎng)度由類型來(lái)決定,如果我們能夠確定每個(gè)字段成員的類型,那么我們不就能夠?qū)⒃擃愋蛯?duì)應(yīng)的字節(jié)數(shù)計(jì)算出來(lái)嗎?實(shí)際上是不行的。

Debug.Assert(Unsafe.SizeOf<ValueTuple<byte, byte>>() == 2);
Debug.Assert(Unsafe.SizeOf<ValueTuple<byte, short>>() == 4);
Debug.Assert(Unsafe.SizeOf<ValueTuple<byte, int>>() == 8);
Debug.Assert(Unsafe.SizeOf<ValueTuple<byte, long>>() == 16);

一上面的程序?yàn)槔?,我們知道byte、short、int和long的字節(jié)數(shù)分別是1、2、4和8,所以一個(gè)針對(duì)byte的二元組的字節(jié)數(shù)為2,但是對(duì)于一個(gè)針對(duì)類型組合分別為byte + short,byte + int,byte + long的二元組來(lái)說(shuō),對(duì)應(yīng)的字節(jié)并不是3、5和9,而是3、8和16。因?yàn)檫@涉及內(nèi)存對(duì)齊(memory alignment)的問(wèn)題。

五、值類型和應(yīng)用類型的布局

對(duì)于完全相同的數(shù)據(jù)成員,引用類型和子類型的實(shí)例所占的字節(jié)數(shù)也是不同的。如下圖所示,值類型實(shí)例的字節(jié)序列全部用來(lái)存儲(chǔ)它的字段成員。對(duì)于引用類型的實(shí)例來(lái)說(shuō),在字段字節(jié)序列前面還存儲(chǔ)了類型對(duì)應(yīng)方法表(Method Table)的地址。方法表幾乎提供了描述類型的所有元數(shù)據(jù),我們正是利用這個(gè)引用來(lái)確定實(shí)例屬于何種類型。在最前面,還具有額外的字節(jié),我們將其稱為Object Header,它不僅僅用來(lái)存儲(chǔ)對(duì)象的鎖定狀態(tài),哈希值也可以緩存在這里。當(dāng)我們創(chuàng)建了一個(gè)引用類型變量時(shí),這個(gè)變量并不是指向?qū)嵗純?nèi)存的首字節(jié),而是存放方法表地址的地方。

六、Ldflda指令

上面我們介紹sizeof操作符和靜態(tài)類型Marshal/Unsafe提供的SizeOf方法均不能真正解決實(shí)例占用字節(jié)長(zhǎng)度的計(jì)算。就我目前的了解,這個(gè)問(wèn)題在單純的C#領(lǐng)域都無(wú)法解決,但I(xiàn)L層面提供的Ldflda指令可以幫助我們解決這個(gè)問(wèn)題。顧名思義,Ldflda表示Load Field Address,它可以幫助我們得到實(shí)例某個(gè)字段的地址。由于這個(gè)IL指令在C#中沒(méi)有對(duì)應(yīng)的API,所以我們只有采用如下的形式采用IL Emit的來(lái)使用它。

public class SizeCalculator
{
    private static Func<object?, long[]> GenerateFieldAddressAccessor(FieldInfo[] fields)
    {
        var method = new DynamicMethod(
            name: "GetFieldAddresses",
            returnType: typeof(long[]),
            parameterTypes: new[] { typeof(object) },
            m: typeof(SizeCalculator).Module,
            skipVisibility: true);
        var ilGen = method.GetILGenerator();
        // var addresses = new long[fields.Length + 1];
        ilGen.DeclareLocal(typeof(long[]));
        ilGen.Emit(OpCodes.Ldc_I4, fields.Length + 1);
        ilGen.Emit(OpCodes.Newarr, typeof(long));
        ilGen.Emit(OpCodes.Stloc_0);
        // addresses[0] = address of instace;
        ilGen.Emit(OpCodes.Ldloc_0);
        ilGen.Emit(OpCodes.Ldc_I4, 0);
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Conv_I8);
        ilGen.Emit(OpCodes.Stelem_I8);
        // addresses[index] = address of field[index + 1];
        for (int index = 0; index < fields.Length; index++)
        {
            ilGen.Emit(OpCodes.Ldloc_0);
            ilGen.Emit(OpCodes.Ldc_I4, index + 1);
            ilGen.Emit(OpCodes.Ldarg_0);
            ilGen.Emit(OpCodes.Ldflda, fields[index]);
            ilGen.Emit(OpCodes.Conv_I8);
            ilGen.Emit(OpCodes.Stelem_I8);
        }
        ilGen.Emit(OpCodes.Ldloc_0);
        ilGen.Emit(OpCodes.Ret);
        return (Func<object?, long[]>)method.CreateDelegate(typeof(Func<object, long[]>));
    }
    ...
}

如上面的代碼片段所示,我們?cè)赟izeCalculator類型中定了一個(gè)GenerateFieldAddressAccessor方法,它會(huì)根據(jù)指定類型的字段列表生成一個(gè)Func<object?, long[]> 類型的委托,該委托幫助我們返回指定對(duì)象及其所有字段的內(nèi)存地址。有了對(duì)象自身的地址和每個(gè)字段的地址,我們自然就可以得到每個(gè)字段的偏移量,進(jìn)而很容易地計(jì)算出整個(gè)實(shí)例所占內(nèi)存的字節(jié)數(shù)。

七、計(jì)算值類型的字節(jié)數(shù)

由于值類型和引用類型在內(nèi)存中采用不同的布局,我們也需要采用不同的計(jì)算方式。由于結(jié)構(gòu)體在內(nèi)存中字節(jié)就是所有字段的內(nèi)容,所有我們采用一種討巧的計(jì)算方法。假設(shè)我們需要結(jié)算類型為T的結(jié)構(gòu)體的字節(jié)數(shù),那么我們創(chuàng)建一個(gè)ValueTuple<T,T>元組,它的第二個(gè)字段Item2的偏移量就是結(jié)構(gòu)體T的字節(jié)數(shù)。具體的計(jì)算方式體現(xiàn)在如下這個(gè)CalculateValueTypeInstance方法中。

public class SizeCalculator
{
    public int CalculateValueTypeInstance(Type type)
    {
        var instance = GetDefaultAsObject(type);
        var fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .Where(it => !it.IsStatic)
            .ToArray();
        if (fields.Length == 0) return 0;
        var tupleType = typeof(ValueTuple<,>).MakeGenericType(type, type);
        var tuple = tupleType.GetConstructors()[0].Invoke(new object?[] { instance, instance });
        var addresses = GenerateFieldAddressAccessor(tupleType.GetFields()).Invoke(tuple).OrderBy(it => it).ToArray();
        return (int)(addresses[2] - addresses[0]);
    }
}

如上面的代碼片段所示, 假設(shè)我們需要計(jì)算的結(jié)構(gòu)體類型為T,我們調(diào)用GetDefaultAsObject方法以反射的形式得到default(T)對(duì)象,進(jìn)而將ValueTuple<T,T>元組創(chuàng)建出來(lái)。在調(diào)用GenerateFieldAddressAccessor方法得到用于計(jì)算實(shí)例及其字段地址的Func<object?, long[]> 委托后,我們將這個(gè)元組作為參數(shù)調(diào)用這個(gè)委托。對(duì)于得到的三個(gè)內(nèi)存地址,代碼元組和第1、2個(gè)字段的地址是相同的,我們使用代表Item2的第三個(gè)地址減去第一個(gè)地址,得到的就是我們希望的結(jié)果。

八、計(jì)算引用類型字節(jié)數(shù)

引用類型的字節(jié)計(jì)算要復(fù)雜一些,具體采用這樣的思路:我們?cè)诘玫綄?shí)例自身和每個(gè)字段的地址后,我們對(duì)地址進(jìn)行排序進(jìn)而得到最后一個(gè)字段的偏移量。我們讓這個(gè)偏移量加上最后一個(gè)字段自身的字節(jié)數(shù),再補(bǔ)充上必要的“頭尾字節(jié)”就是我們希望得到的結(jié)果,具體計(jì)算體現(xiàn)在如下這個(gè)CalculateReferneceTypeInstance方法上。

public class SizeCalculator
{
    public int CalculateReferenceTypeInstance(Type type, object instance)
    {
        var fields = GetBaseTypesAndThis(type)
            .SelectMany(type => type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
            .Where(it => !it.IsStatic).ToArray();
        if (fields.Length == 0) return type.IsValueType ? 0 : 3 * IntPtr.Size;
        var addresses = GenerateFieldAddressAccessor(fields).Invoke(instance);
        var list = new List<FieldInfo>(fields);
        list.Insert(0, null!);
        fields = list.ToArray();
        Array.Sort(addresses, fields);
        var lastFieldOffset = (int)(addresses.Last() - addresses.First());
        var lastField = fields.Last();
        var lastFieldSize = lastField.FieldType.IsValueType ? CalculateValueTypeInstance(lastField.FieldType) : IntPtr.Size;
        var size = lastFieldOffset + lastFieldSize;
        // Round up to IntPtr.Size
        int round = IntPtr.Size - 1;
        return ((size + round) & (~round)) + IntPtr.Size;
        static IEnumerable<Type> GetBaseTypesAndThis(Type? type)
        {
            while (type is not null)
            {
                yield return type;
                type = type.BaseType;
            }
        }
    }

如上面的代碼片段所示,如果指定的類型沒(méi)有定義任何字段,CalculateReferneceTypeInstance 返回引用類型實(shí)例的最小字節(jié)數(shù):3倍地址指針字節(jié)數(shù)。對(duì)于x86架構(gòu),一個(gè)應(yīng)用類型對(duì)象至少占用12字節(jié),包括ObjectHeader(4 bytes)、方法表指針(bytes)和最少4字節(jié)的字段內(nèi)容(即使沒(méi)有類型沒(méi)有定義任何字段,這個(gè)4個(gè)字節(jié)也是必需的)。如果是x64架構(gòu),這個(gè)最小字節(jié)數(shù)會(huì)變成24,因?yàn)榉椒ū碇羔樅妥钚∽侄蝺?nèi)容變成了8個(gè)字節(jié),雖然ObjectHeader的有效內(nèi)容只占用4個(gè)字節(jié),但是前面會(huì)添加4個(gè)字節(jié)的Padding。

對(duì)于最后字段所占字節(jié)的結(jié)算也很簡(jiǎn)單:如果類型是值類型,那么就調(diào)用前面定義的CalculateValueTypeInstance方法進(jìn)行計(jì)算,如果是引用類型,字段存儲(chǔ)的內(nèi)容僅僅是目標(biāo)對(duì)象的內(nèi)存地址,所以長(zhǎng)度就是IntPtr.Size。由于引用類型實(shí)例在內(nèi)存中默認(rèn)會(huì)采用IntPtr.Size對(duì)齊,這里也做了相應(yīng)的處理。最后不要忘了,引用類型實(shí)例的引用指向的并不是內(nèi)存的第一個(gè)字節(jié),而是存放方法表指針的字節(jié),所以還得加上ObjecthHeader 字節(jié)數(shù)(IntPtr.Size)。

九、完整的計(jì)算

分別用來(lái)計(jì)算值類型和引用類型實(shí)例字節(jié)數(shù)的兩個(gè)方法被用在如下這個(gè)SizeOf方法中。由于Ldflda指令的調(diào)用需要提供對(duì)應(yīng)的實(shí)例,所以該方法除了提供目標(biāo)類型外,還提供了一個(gè)用來(lái)獲得對(duì)應(yīng)實(shí)例的委托。該委托對(duì)應(yīng)的參數(shù)是可以缺省的,對(duì)于值類型,我們會(huì)使用默認(rèn)值。對(duì)于引用類型,我們也會(huì)試著使用默認(rèn)構(gòu)造函數(shù)來(lái)創(chuàng)建目標(biāo)對(duì)象。如果沒(méi)有提供此委托對(duì)象,也無(wú)法創(chuàng)建目標(biāo)實(shí)例,SizeOf方法會(huì)拋出異常。雖然需要提供目標(biāo)實(shí)例,但是計(jì)算出的結(jié)果只和類型有關(guān),所以我們將計(jì)算結(jié)果進(jìn)行了緩存。為了調(diào)用方便,我們還提供了另一個(gè)泛型的SizeOf<T>方法。

public class SizeCalculator
{
    private static readonly ConcurrentDictionary<Type, int> _sizes = new();
    public static readonly SizeCalculator Instance = new();
    public int SizeOf(Type type, Func<object?>? instanceAccessor = null)
    {
        if (_sizes.TryGetValue(type, out var size)) return size;
        if (type.IsValueType) return _sizes.GetOrAdd(type, CalculateValueTypeInstance);
        object? instance;
        try
        {
            instance = instanceAccessor?.Invoke() ?? Activator.CreateInstance(type);
        }
        catch
        {
            throw new InvalidOperationException("The delegate to get instance must be specified.");
        }
        return _sizes.GetOrAdd(type, type => CalculateReferenceTypeInstance(type, instance));
    }
    public int SizeOf<T>(Func<T>? instanceAccessor = null)
    {
        if (instanceAccessor is null) return SizeOf(typeof(T));
        Func<object?> accessor = () => instanceAccessor();
        return SizeOf(typeof(T), accessor);
    }
}

在如下的代碼片段中,我們使用它輸出了兩個(gè)具有相同字段定義的結(jié)構(gòu)體和類型的字節(jié)數(shù)。在下一篇文章中,我們將進(jìn)一步根據(jù)計(jì)算出的字節(jié)數(shù)得到實(shí)例在內(nèi)存中的完整二進(jìn)制內(nèi)容,敬請(qǐng)關(guān)注。

Debug.Assert( SizeCalculator.Instance.SizeOf<FoobarStructure>() == 16);
Debug.Assert( SizeCalculator.Instance.SizeOf<FoobarClass>() == 32);
public struct FoobarStructure
{
    public byte Foo;
    public long Bar;
}
public class FoobarClass
{
    public byte Foo;
    public long Bar;
}

以上就是詳解C#如何計(jì)算一個(gè)實(shí)例占用多少內(nèi)存的詳細(xì)內(nèi)容,更多關(guān)于C#計(jì)算內(nèi)存的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C# WinForm捕獲未處理的異常實(shí)例解析

    C# WinForm捕獲未處理的異常實(shí)例解析

    這篇文章主要介紹了C# WinForm捕獲未處理的異常,包括了常見(jiàn)的未捕獲的異常、UI線程異常、非UI線程異常等,非常實(shí)用,需要的朋友可以參考下
    2014-09-09
  • 基于C#生成條形碼操作知識(shí)匯總附源碼下載

    基于C#生成條形碼操作知識(shí)匯總附源碼下載

    這篇文章主要介紹了基于C#生成條形碼操作知識(shí)匯總的相關(guān)資料,需要的朋友可以參考下
    2015-12-12
  • C#中Settings全局配置詳解

    C#中Settings全局配置詳解

    本文主要介紹了C#中Settings全局配置詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • C#常見(jiàn)應(yīng)用函數(shù)實(shí)例小結(jié)

    C#常見(jiàn)應(yīng)用函數(shù)實(shí)例小結(jié)

    這篇文章主要介紹了C#常見(jiàn)應(yīng)用函數(shù),結(jié)合實(shí)例形式總結(jié)分析了C#常用的時(shí)間、URL、HTML、反射、小數(shù)運(yùn)算等相關(guān)函數(shù),需要的朋友可以參考下
    2017-01-01
  • C#實(shí)現(xiàn)終止正在執(zhí)行的線程

    C#實(shí)現(xiàn)終止正在執(zhí)行的線程

    這篇文章主要介紹了C#實(shí)現(xiàn)終止正在執(zhí)行的線程的方法,針對(duì)臨界資源等容易出現(xiàn)錯(cuò)誤的地方進(jìn)行了分析,并提出了改進(jìn)方案與實(shí)例,需要的朋友可以參考下
    2014-09-09
  • C#判斷指定分區(qū)是否是ntfs格式的方法

    C#判斷指定分區(qū)是否是ntfs格式的方法

    這篇文章主要介紹了C#判斷指定分區(qū)是否是ntfs格式的方法,涉及C#中DriveFormat屬性的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-04-04
  • C#入門之定義類成員與接口實(shí)現(xiàn)

    C#入門之定義類成員與接口實(shí)現(xiàn)

    這篇文章介紹了C#入門之定義類成員與接口實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • C#使用遠(yuǎn)程服務(wù)調(diào)用框架Apache Thrift

    C#使用遠(yuǎn)程服務(wù)調(diào)用框架Apache Thrift

    這篇文章介紹了C#使用遠(yuǎn)程服務(wù)調(diào)用框架Apache Thrift的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • C#特性(Attribute)

    C#特性(Attribute)

    這篇文章介紹了C#的特性(Attribute),文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • C# 6.0 新特性匯總

    C# 6.0 新特性匯總

    這篇文章主要介紹了C# 6.0 新特性匯總的相關(guān)資料,本文給大家?guī)?lái)了11種新特征,非常不錯(cuò),感興趣的朋友一起看看吧
    2016-09-09

最新評(píng)論