Skip to content
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

MethodInfo Invoke with [Optional] argument as Type.Missing fails #100322

Closed
jborean93 opened this issue Mar 27, 2024 · 4 comments
Closed

MethodInfo Invoke with [Optional] argument as Type.Missing fails #100322

jborean93 opened this issue Mar 27, 2024 · 4 comments

Comments

@jborean93
Copy link
Contributor

Description

I'm trying to invoke a method that contains an OptionalAttribute without a default value set using reflection but it fails when using Type.Missing with

System.ArgumentException: Object of type 'System.Reflection.Missing' cannot be converted to type 'System.Int32'.

Reproduction Steps

using System;
using System.Runtime.InteropServices;

public class Program
{
    public static void Main()
    {
        int value1 = (int)typeof(Program).GetMethod(nameof(TestMethod1))
            .Invoke(null, new[] { Type.Missing });
        Console.WriteLine($"TestMethod1() => {value1}");

        int value2 = (int)typeof(Program).GetMethod(nameof(TestMethod2))
            .Invoke(null, new[] { Type.Missing });
        Console.WriteLine($"TestMethod2() => {value2}");

        int value3 = (int)typeof(Program).GetMethod(nameof(TestMethod3))
            .Invoke(null, new[] { Type.Missing });
        Console.WriteLine($"TestMethod3() => {value3}");
    }

    public static int TestMethod1(int value = 1) => value;
    public static int TestMethod2([Optional, DefaultParameterValue(1)] int value) => value;
    public static int TestMethod3([Optional] int value) => value;
}

Expected behavior

TestMethod1() => 1
TestMethod2() => 1
TestMethod3() => 0

Actual behavior

TestMethod1() => 1
TestMethod2() => 1
Unhandled exception. System.ArgumentException: Object of type 'System.Reflection.Missing' cannot be converted to type 'System.Int32'.
   at System.RuntimeType.CheckValue(Object& value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Program.Main() in /home/user/dev/Example/Program.cs:line 16

Regression?

It does not look like it, the same example fails on .NET Framework 4.8.

Known Workarounds

The workaround is to pass in null (I'm not sure if that works in all scenarios or not).

int value3 = (int)typeof(Program).GetMethod(nameof(TestMethod3))
            .Invoke(null, new object[] { null });

Configuration

net8.0
All OS
Tested on x64
I don't believe this is specific to this configuration.

Other information

This is opened in response to PowerShell/PowerShell#21372. PowerShell is using an ExpressionTree to invoke the method. It uses the ParameterInfo.DefaultValue for arguments not specified in PowerShell which for TestMethod3() is Missing.

Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;

public class MyClass
{
    public static int TestMethod1(int value = 1) => value;
    public static int TestMethod2([Optional, DefaultParameterValue(1)] int value) => value;
    public static int TestMethod3([Optional] int value) => value;
}
'@

[MyClass].GetMethod('TestMethod1').GetParameters() | Select-Object Attributes, DefaultValue

          Attributes DefaultValue
          ---------- ------------
Optional, HasDefault            1

[MyClass].GetMethod('TestMethod2').GetParameters() | Select-Object Attributes, DefaultValue

          Attributes DefaultValue
          ---------- ------------
Optional, HasDefault            1

[MyClass].GetMethod('TestMethod3').GetParameters() | Select-Object Attributes, DefaultValue

Attributes DefaultValue
---------- ------------
  Optional System.Reflection.Missing

I've got a patch for PowerShell that uses Expression.Default(parameters[0].ParameterType); which solves this issue but it seems like this might be a bug in .NET that should be fixed here. If not it would be great to understand more about why the current behaviour fails and if there are other cases were ParameterInfo.DefaultType might also be System.Reflection.Missing.

Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-reflection
See info in area-owners.md if you want to be subscribed.

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Mar 27, 2024
@steveharter steveharter added bug and removed untriaged New issue has not been triaged by the area owner labels Mar 27, 2024
@steveharter steveharter self-assigned this Mar 27, 2024
@steveharter steveharter removed the bug label Mar 27, 2024
@steveharter
Copy link
Member

This is by design; see the test Invoke_OptionalParameterUnassingableFromMissing_WithMissingValue_ThrowsArgumentException. where the parameter type is string. The similar test before that has an object parameter type and doesn't throw but the incoming value is Type.Missing which can be passed as object.

Also verified previous versions (v7, v8) are consistent.

@steveharter steveharter closed this as not planned Won't fix, can't repro, duplicate, stale Mar 27, 2024
@jborean93
Copy link
Contributor Author

Thanks @steveharter for replying, so is the solution here to just provide null for these values explicitly? Are you aware of any other scenario where ParameterInfo.DefaultValue might also by System.Reflection.Missing that could potential cause problems if I change the PowerShell code to provide the default value/null for this argument when calling it?

@jborean93
Copy link
Contributor Author

jborean93 commented Mar 27, 2024

Oh and the linked issue for that test seems to be unrelated mono/mono#15025 so not sure what the reason behind this behaviour is.

@github-actions github-actions bot locked and limited conversation to collaborators Apr 27, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants