-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
For static extern methods, C# should support passing null
to any out
or ref
parameters marked with [Optional]
.
#13119
Comments
I would love this. It would save maintaining a plethora of alternate signatures with IntPtr in place of ref/out just so I can pass IntPtr.Zero. |
I agree that it would be great if this specific scenario was improved, but I don't like that this proposal adds special behavior that only applies to What if passing Also, how would this proposal look like at IL level? Since calling an |
@svick, I attempted to address your first thought (about working everywhere) in the I agree working with all methods would be great, but as you mentioned, there would need to be language (and possibly CLR support) for getting it to work everywhere. I don't think using a special field will work either (without CLR support), as the native code called into wouldn't see the variable as I think Namely:
and
|
It would be just as useful in non-extern methods, I agree. Would the value in the temporary out variable be disposed if it implements |
@jnm2, the temporary out variable would need to be aware of the exact semantics of the unmanaged allocation (meaning, is it COM, is it Heap Allocated, is it CoTaskMem, was it allocated by a call to malloc or Edit I may have misread your comment. If the compiler generated a temporary variable (as it does with In either case, having a temporary variable that is immediately or indirectly disposed introduces some overhead that is undesirable. The primary goal is to keep interop code easy to write and to allow the user to prevent the allocation entirely when a method supports it. |
The new "out variable declaration" feature improves this use case, and in the future we expect to support |
@gafter How is |
The new A lot of unmanaged functions that allow the HRESULT WINAPI D3D12CreateDevice(
_In_opt_ IUnknown *pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
_In_ REFIID riid,
_Out_opt_ void **ppDevice
);
// This code validates that the default adapter supports Direct3D12, but does not
// actually do the expensive operation of creating the device
D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_0, __uuidof(ID3D12Device), nullptr);
// This code validates that the default adapter supports Direct3D12 and also
// creates the device if it is supported.
ID3D12Device* pD3DDevice = nullptr;
D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_0, __uuidof(ID3D12Device), &pD3DDevice); Using just the This issue is proposing that, for an external method in the format of: public enum D3D_FEATURE_LEVEL
{
// Enumeration Values
}
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 4)]
public struct HRESULT
{
private int _value;
}
[Guid("2411E7E1-12AC-4CCF-BD14-9798E8534DC0"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe public interface IDXGIAdapter : IDXGIObject
{
// Method Declarations
}
[Guid("189819F1-1DB6-4B57-BE54-1821339B85F7"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe public interface ID3D12Device : ID3D12Object
{
// Method Declarations
}
[DllImport("D3D12.dll", BestFitMapping = false, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, EntryPoint = "D3D12CreateDevice", ExactSpelling = true, PreserveSig = true, SetLastError = false, ThrowOnUnmappableChar = false)]
public static extern HRESULT D3D12CreateDevice(
[In, Optional, MarshalAs(UnmanagedType.Interface)] IDXGIAdapter pAdapter,
[In] D3D_FEATURE_LEVEL MinimumFeatureLevel,
[In] ref Guid riid,
[Out, Optional, MarshalAs(UnmanagedType.Interface)] out ID3D12Device ppDevice
); The compiler should support a calling var riid = typeof(ID3D12Device).GUID;
D3D12CreateDevice(null, D3D_FEATURE_LEVEL._12_0, ref riid, out null); This would cause the first code path to execute (where the expensive operation is not performed) because the compiler passes 'null' to the final parameter. As mentioned in the OP, there are several workarounds (such as using |
I believe you'd need P/Invoke marshaling support (i.e. adding some code in CLR) to do this right. Even when the compiler knows that the method is However, instead of generating them, P/Invoke can just offer a public generic class generating such wrappers - i.e. somewhere in public static class NullPointer<T> {
public T Value;
} and then, whenever P/Invoke marshaler sees that the parameter is In fact, with this addition, there's no need for language support at all. The only thing that compiler would be doing is desugaring |
Another possibility is to reuse |
Ported to dotnet/csharplang#79 |
Issue
There are a number of interop scenarios where a static extern method takes an out parameter and allows it to be
null
.For Example
might translate to
However, C# does not currently support passing
null
to the optional fourth parameter.Workaround
Use unsafe code and remove the
out
keyword so that the user can actually passnull
.For example, the above becomes:
Proposal
For static extern methods, C# should support passing
null
to anyout
orref
parameter marked withOptional
.This would allow you to write code such as:
Other Thoughts
It would be immensely useful if this was supported in other scenarios as well, such as for COM Interop.
For example, one might modify the above function to take a strongly type interface:
The proposal would work for the static extern method, but it would not work for the
ID3D12Device.CreateHeap
method.Support for the static extern method is much simpler, because the code is guaranteed to not exist in managed code.
However, support for the latter would require some extension to the language. Such an extension would either indicate the interface/delegate/method exist in native code (and can therefore handle a
null
out parameter) or it would allow the language itself to work withnull
out parameters (currently there is no way to determine that theout
is null, only that the objectout
points to isnull
).It should be noted that
[ComImport]
does provide some of the functionality desired for optional out parameters on interfaces. However, applying theComImport
attribute may provide functionality that is undesirable in some COM interop scenarios.The text was updated successfully, but these errors were encountered: