Aracılığıyla paylaş


Yapı hazırlamayı özelleştirme

Bazen yapılar için varsayılan sıralama kuralları tam olarak ihtiyacınız olan şey değildir. .NET çalışma zamanları, yapınızın düzenini ve alanların nasıl düzenlendiğini özelleştirmeniz için birkaç uzantı noktası sağlar. Yapı düzenini özelleştirme tüm senaryolar için desteklenir, ancak alan sıralamasını özelleştirme yalnızca çalışma zamanı hazırlamanın etkinleştirildiği senaryolarda desteklenir. Çalışma zamanı hazırlama devre dışı bırakıldıysa, tüm alan hazırlamaları el ile yapılmalıdır.

Not

Bu makale, kaynak tarafından oluşturulan birlikte çalışma için hazırlamayı özelleştirmeyi kapsamaz. P/Invokes veya COM için kaynak tarafından oluşturulan birlikte çalışma kullanıyorsanız bkz. Sıralamayı özelleştirme.

Yapı düzenini özelleştirme

.NET, alanların belleğe yerleştirilme şeklini özelleştirmenize olanak sağlamak için özniteliğini ve System.Runtime.InteropServices.LayoutKind numaralandırmasını sağlarSystem.Runtime.InteropServices.StructLayoutAttribute. Aşağıdaki kılavuz yaygın sorunlardan kaçınmanıza yardımcı olacaktır.

✔️ Mümkün olduğunda kullanmayı LayoutKind.Sequential GÖZ ÖNÜNDE BULUNDURUN.

✔️ DO, yalnızca LayoutKind.Explicit yerel yapınızda birleşim gibi açık bir düzen olduğunda kullanılır.

❌ Devralma yoluyla karmaşık yerel türleri ifade etmek için sınıfları kullanmaktan KAÇıNıN.

❌ .NET Core 3.0'ın öncesinde çalışma zamanlarını hedeflemeniz gerekiyorsa, Windows dışı platformlarda yapıları derlerken kullanmaktan LayoutKind.Explicit KAÇıNıN. 3.0 öncesi .NET Core çalışma zamanı, Intel veya AMD 64 bit Windows olmayan sistemlerde yerel işlevlere değere göre açık yapıların geçirilmesini desteklemez. Ancak, çalışma zamanı tüm platformlarda başvuruya göre açık yapıların geçirilmesini destekler.

Boole alanı sıralamasını özelleştirme

Yerel kodun birçok farklı Boole gösterimi vardır. Yalnızca Windows'ta Boole değerlerini temsil etmenin üç yolu vardır. Çalışma zamanı yapınızın yerel tanımını bilmiyor, bu nedenle en iyisi Boole değerlerinizi nasıl sıralayabileceğinizi tahmin etmektir. .NET çalışma zamanı, Boole alanınızı hazırlamayı göstermek için bir yol sağlar. Aşağıdaki örneklerde .NET'in bool farklı yerel Boole türlerine nasıl sıralandığı gösterilmektedir.

Boole değerleri, aşağıdaki örnekte gösterildiği gibi varsayılan olarak yerel 4 baytlık Win32 BOOL değeri olarak sıralı hale getirmektir:

public struct WinBool
{
    public bool b;
}
struct WinBool
{
    public BOOL b;
};

Açık olmak istiyorsanız, yukarıdakiyle UnmanagedType.Bool aynı davranışı elde etmek için değerini kullanabilirsiniz:

public struct WinBool
{
    [MarshalAs(UnmanagedType.Bool)]
    public bool b;
}
struct WinBool
{
    public BOOL b;
};

UnmanagedType.U1 Aşağıdaki veya UnmanagedType.I1 değerlerini kullanarak çalışma zamanına alanı 1 baytlık yerel bool bir tür olarak hazırlamasını b söyleyebilirsiniz.

public struct CBool
{
    [MarshalAs(UnmanagedType.U1)]
    public bool b;
}
struct CBool
{
    public bool b;
};

Windows'da, çalışma zamanına UnmanagedType.VariantBool Boole değerinizi 2 baytlık VARIANT_BOOL bir değere hazırlamasını bildirmek için değerini kullanabilirsiniz:

public struct VariantBool
{
    [MarshalAs(UnmanagedType.VariantBool)]
    public bool b;
}
struct VariantBool
{
    public VARIANT_BOOL b;
};

Not

VARIANT_BOOL ve içindeki bool türlerinin çoğundan VARIANT_TRUE = -1VARIANT_FALSE = 0farklıdır. Buna ek olarak, eşit VARIANT_TRUE olmayan tüm değerler false olarak kabul edilir.

Dizi alanı sıralamasını özelleştirme

.NET ayrıca dizi sıralamasını özelleştirmek için birkaç yol içerir.

Varsayılan olarak, .NET dizileri öğelerin bitişik bir listesinin işaretçisi olarak sıralar:

public struct DefaultArray
{
    public int[] values;
}
struct DefaultArray
{
    int32_t* values;
};

COM API'leriyle birlikte kullanıyorsanız dizileri nesne olarak SAFEARRAY* sıralamanız gerekebilir. ve UnmanagedType.SafeArray değerini kullanarak System.Runtime.InteropServices.MarshalAsAttribute çalışma zamanına bir diziyi olarak SAFEARRAY*hazırlamasını bildirebilirsiniz:

public struct SafeArrayExample
{
    [MarshalAs(UnmanagedType.SafeArray)]
    public int[] values;
}
struct SafeArrayExample
{
    SAFEARRAY* values;
};

içinde SAFEARRAYne tür bir öğe olduğunu özelleştirmeniz gerekiyorsa ve MarshalAsAttribute.SafeArrayUserDefinedSubType alanlarını kullanarak MarshalAsAttribute.SafeArraySubType öğesinin tam öğe türünü SAFEARRAYözelleştirebilirsiniz.

Diziyi yerinde hazırlamanız gerekiyorsa, değerini kullanarak UnmanagedType.ByValArray marshaller'a diziyi yerinde hazırlamasını söyleyebilirsiniz. Bu sıralamayı kullanırken, çalışma zamanının MarshalAsAttribute.SizeConst yapı için doğru alan ayırabilmesi için dizideki öğe sayısı için alana bir değer de sağlamanız gerekir.

public struct InPlaceArray
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public int[] values;
}
struct InPlaceArray
{
    int values[4];
};

Not

.NET, değişken uzunlukta bir dizi alanının C99 Esnek Dizi Üyesi olarak sıralanmasını desteklemez.

Dize alanı sıralamasını özelleştirme

.NET ayrıca dize alanlarını sıralamak için çok çeşitli özelleştirmeler sağlar.

Varsayılan olarak, .NET bir dizeyi null olarak sonlandırılan bir dizeye işaretçi olarak sıralar. Kodlama, içindeki alanının System.Runtime.InteropServices.StructLayoutAttributedeğerine StructLayoutAttribute.CharSet bağlıdır. Öznitelik belirtilmezse, kodlama varsayılan olarak ANSI kodlaması olur.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DefaultString
{
    public string str;
}
struct DefaultString
{
    char* str;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DefaultString
{
    public string str;
}
struct DefaultString
{
    char16_t* str; // Could also be wchar_t* on Windows.
};

Farklı alanlar için farklı kodlamalar kullanmanız gerekiyorsa veya yapı tanımınızda daha açık olmak istiyorsanız veya değerlerini bir System.Runtime.InteropServices.MarshalAsAttribute öznitelikte kullanabilirsiniz.UnmanagedType.LPStrUnmanagedType.LPWStr

public struct AnsiString
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string str;
}
struct AnsiString
{
    char* str;
};
public struct UnicodeString
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public string str;
}
struct UnicodeString
{
    char16_t* str; // Could also be wchar_t* on Windows.
};

DIZElerinizi UTF-8 kodlamasını kullanarak sıralamak istiyorsanız, içindeki MarshalAsAttributedeğerini kullanabilirsinizUnmanagedType.LPUTF8Str.

public struct UTF8String
{
    [MarshalAs(UnmanagedType.LPUTF8Str)]
    public string str;
}
struct UTF8String
{
    char* str;
};

Not

Kullanmak UnmanagedType.LPUTF8Str için .NET Framework 4.7 (veya sonraki sürümler) veya .NET Core 1.1 (veya sonraki sürümler) gerekir. .NET Standard 2.0'da kullanılamaz.

COM API'leriyle çalışıyorsanız, dizeyi olarak BSTRhazırlamanız gerekebilir. UnmanagedType.BStr değerini kullanarak bir dizeyi olarak BSTRsıralayabilirsiniz.

public struct BString
{
    [MarshalAs(UnmanagedType.BStr)]
    public string str;
}
struct BString
{
    BSTR str;
};

WinRT tabanlı API kullanırken, dizeyi olarak HSTRINGhazırlamanız gerekebilir. UnmanagedType.HString değerini kullanarak bir dizeyi olarak HSTRINGsıralayabilirsiniz. HSTRING marshalling yalnızca yerleşik WinRT desteğine sahip çalışma zamanlarında desteklenir. .NET 5'te WinRT desteği kaldırıldı, bu nedenle HSTRING .NET 5 veya daha yeni sürümlerde marshalling desteklenmez.

public struct HString
{
    [MarshalAs(UnmanagedType.HString)]
    public string str;
}
struct BString
{
    HSTRING str;
};

API'niz dizeyi yapıda yerinde geçirmenizi gerektiriyorsa değerini kullanabilirsiniz UnmanagedType.ByValTStr . tarafından sıralanmış ByValTStr bir dize için kodlamanın özniteliğinden CharSet belirlendiğini unutmayın. Ayrıca, alan tarafından bir dize uzunluğu geçirilmesini MarshalAsAttribute.SizeConst gerektirir.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DefaultString
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string str;
}
struct DefaultString
{
    char str[4];
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DefaultString
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string str;
}
struct DefaultString
{
    char16_t str[4]; // Could also be wchar_t[4] on Windows.
};

Ondalık alan sıralamasını özelleştirme

Windows üzerinde çalışıyorsanız, yerel CY veya CURRENCY yapıyı kullanan bazı API'lerle karşılaşabilirsiniz. Varsayılan olarak, .NET decimal türü yerel DECIMAL yapıya göre sıralar. Ancak, bir değeri yerel bir değere dönüştürmesi decimal için marshaller'a yönerge vermek için CY değeri ile kullanabilirsiniz MarshalAsAttributeUnmanagedType.Currency.

public struct Currency
{
    [MarshalAs(UnmanagedType.Currency)]
    public decimal dec;
}
struct Currency
{
    CY dec;
};

Birleşimler

Birleşim, aynı belleğin üzerinde farklı veri türleri içerebilen bir veri türüdür. Bu, C dilindeki yaygın bir veri biçimidir. Birleşim kullanılarak .NET'te LayoutKind.Explicitifade edilebilir. .NET'te birleşim tanımlarken yapıların kullanılması önerilir. Sınıfların kullanılması düzen sorunlarına neden olabilir ve öngörülemeyen davranışlara neden olabilir.

struct device1_config
{
    void* a;
    void* b;
    void* c;
};
struct device2_config
{
    int32_t a;
    int32_t b;
};
struct config
{
    int32_t type;

    union
    {
        device1_config dev1;
        device2_config dev2;
    };
};
public unsafe struct Device1Config
{
    void* a;
    void* b;
    void* c;
}

public struct Device2Config
{
    int a;
    int b;
}

public struct Config
{
    public int Type;

    public _Union Anonymous;

    [StructLayout(LayoutKind.Explicit)]
    public struct _Union
    {
        [FieldOffset(0)]
        public Device1Config Dev1;

        [FieldOffset(0)]
        public Device2Config Dev2;
    }
}

Mareşal System.Object

Windows'da, türü belirtilen alanları yerel koda göre sıralayabilirsiniz object. Bu alanları üç türden birine göre sıralayabilirsiniz:

Varsayılan olarak, object-typed alanı nesneyi sarmalayan bir IUnknown* alana göre hazırlanır.

public struct ObjectDefault
{
    public object obj;
}
struct ObjectDefault
{
    IUnknown* obj;
};

Bir nesne alanını öğesine sıralamak IDispatch*istiyorsanız, değeriyle birlikte UnmanagedType.IDispatch bir MarshalAsAttribute ekleyin.

public struct ObjectDispatch
{
    [MarshalAs(UnmanagedType.IDispatch)]
    public object obj;
}
struct ObjectDispatch
{
    IDispatch* obj;
};

Bunu olarak VARIANTsıralamak istiyorsanız, değeriyle bir MarshalAsAttributeUnmanagedType.Struct ekleyin.

public struct ObjectVariant
{
    [MarshalAs(UnmanagedType.Struct)]
    public object obj;
}
struct ObjectVariant
{
    VARIANT obj;
};

Aşağıdaki tabloda, alanın farklı çalışma zamanı türlerinin obj içinde VARIANTdepolanan çeşitli türlerle nasıl eşlediği açıklanmaktadır:

.NET Türü VARIANT Türü
byte VT_UI1
sbyte VT_I1
short VT_I2
ushort VT_UI2
int VT_I4
uint VT_UI4
long VT_I8
ulong VT_UI8
float VT_R4
double VT_R8
char VT_UI2
string VT_BSTR
System.Runtime.InteropServices.BStrWrapper VT_BSTR
object VT_DISPATCH
System.Runtime.InteropServices.UnknownWrapper VT_UNKNOWN
System.Runtime.InteropServices.DispatchWrapper VT_DISPATCH
System.Reflection.Missing VT_ERROR
(object)null VT_EMPTY
bool VT_BOOL
System.DateTime VT_DATE
decimal VT_DECIMAL
System.Runtime.InteropServices.CurrencyWrapper VT_CURRENCY
System.DBNull VT_NULL