Skip to content

Commit 868ef08

Browse files
Add notes about native unions. (#33698)
* Add notes about native unions. Co-authored-by: Genevieve Warren <[email protected]>
1 parent 5ff1a04 commit 868ef08

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

docs/standard/native-interop/best-practices.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The guidance in this section applies to all interop scenarios.
1414
- ✔️ DO use the same naming and capitalization for your methods and parameters as the native method you want to call.
1515
- ✔️ CONSIDER using the same naming and capitalization for constant values.
1616
- ✔️ DO use .NET types that map closest to the native type. For example, in C#, use `uint` when the native type is `unsigned int`.
17+
- ✔️ DO prefer expressing higher level native types using .NET structs rather than classes.
1718
- ✔️ DO only use `[In]` and `[Out]` attributes when the behavior you want differs from the default behavior.
1819
- ✔️ CONSIDER using <xref:System.Buffers.ArrayPool%601?displayProperty=nameWithType> to pool your native array buffers.
1920
- ✔️ CONSIDER wrapping your P/Invoke declarations in a class with the same name and capitalization as your native library.
@@ -328,6 +329,8 @@ Pointers to structs in definitions must either be passed by `ref` or use `unsafe
328329

329330
✔️ DO use the C# `sizeof()` instead of `Marshal.SizeOf<MyStruct>()` for blittable structures to improve performance.
330331

332+
❌ AVOID using classes to express complex native types through inheritance.
333+
331334
❌ AVOID using `System.Delegate` or `System.MulticastDelegate` fields to represent function pointer fields in structures.
332335

333336
Since <xref:System.Delegate?displayProperty=fullName> and <xref:System.MulticastDelegate?displayProperty=fullName> don't have a required signature, they don't guarantee that the delegate passed in will match the signature the native code expects. Additionally, in .NET Framework and .NET Core, marshalling a struct containing a `System.Delegate` or `System.MulticastDelegate` from its native representation to a managed object can destabilize the runtime if the value of the field in the native representation isn't a function pointer that wraps a managed delegate. In .NET 5 and later versions, marshalling a `System.Delegate` or `System.MulticastDelegate` field from a native representation to a managed object is not supported. Use a specific delegate type instead of `System.Delegate` or `System.MulticastDelegate`.

docs/standard/native-interop/customize-struct-marshalling.md

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Sometimes the default marshalling rules for structures aren't exactly what you n
1919

2020
✔️ DO only use `LayoutKind.Explicit` in marshalling when your native struct also has an explicit layout, such as a union.
2121

22+
❌ AVOID using classes to express complex native types through inheritance.
23+
2224
❌ AVOID using `LayoutKind.Explicit` when marshalling structures on non-Windows platforms if you need to target runtimes before .NET Core 3.0. The .NET Core runtime before 3.0 doesn't support passing explicit structures by value to native functions on Intel or AMD 64-bit non-Windows systems. However, the runtime supports passing explicit structures by reference on all platforms.
2325

2426
## Customizing Boolean field marshalling
@@ -111,7 +113,7 @@ public struct DefaultArray
111113
```cpp
112114
struct DefaultArray
113115
{
114-
int* values;
116+
int32_t* values;
115117
};
116118
```
117119

@@ -329,6 +331,66 @@ struct Currency
329331
};
330332
```
331333

334+
### Unions
335+
336+
A union is a data type that can contain different types of data atop the same memory. It's a common form of data in the C language. A union can be expressed in .NET using `LayoutKind.Explicit`. It's recommended to use structs when defining a union in .NET. Using classes can cause layout issues and produce unpredictable behavior.
337+
338+
```cpp
339+
struct device1_config
340+
{
341+
void* a;
342+
void* b;
343+
void* c;
344+
};
345+
struct device2_config
346+
{
347+
int32_t a;
348+
int32_t b;
349+
};
350+
struct config
351+
{
352+
int32_t type;
353+
354+
union
355+
{
356+
device1_config dev1;
357+
device2_config dev2;
358+
};
359+
};
360+
```
361+
362+
```csharp
363+
public unsafe struct Device1Config
364+
{
365+
void* a;
366+
void* b;
367+
void* c;
368+
}
369+
370+
public struct Device2Config
371+
{
372+
int a;
373+
int b;
374+
}
375+
376+
public struct Config
377+
{
378+
public int Type;
379+
380+
public _Union Anonymous;
381+
382+
[StructLayout(LayoutKind.Explicit)]
383+
public struct _Union
384+
{
385+
[FieldOffset(0)]
386+
public Device1Config Dev1;
387+
388+
[FieldOffset(0)]
389+
public Device2Config Dev2;
390+
}
391+
}
392+
```
393+
332394
## Marshal `System.Object`
333395

334396
On Windows, you can marshal `object`-typed fields to native code. You can marshal these fields to one of three types:

0 commit comments

Comments
 (0)