3

Given a list of objects to be passed to a method with overloads, I want to determine what overload best matches the object types in order to invoke that method.

string TheMethod(string? foo, string? bar); // one overload
string TheMethod(string? foo);              // another overload
List<object?> arguments = [...];

switch (arguments)
{
    case [string foo, string bar]:
        return TheMethod(foo, bar);
    case [string foo]:
        return TheMethod(foo);
}

This works fine when all arguments are non-null, however, I need to account for the possibility of null values. Other polymorphic types doesn't seem to have this problem.

Is there a way to write patterns such that I can match values that are assignable to the type (specifically for null)?

There are convoluted ways I could write it to allow for the possibility of null values, but that's not fun:

switch (arguments)
{
    case [var foo, var bar] when (foo is null or string && bar is null or string):
        return TheMethod(foo as string, bar as string);
    ...
    case [string? foo, string? bar]: // this would be nice, but illegal
}

Is this the best we can do currently?

3
  • 1
    unfortunately, seems like it. related answer Commented May 14 at 19:02
  • What do you expect to happen if there are two parameters and one of them is not null or string? Commented May 14 at 19:14
  • @Charlieface: In such a case, the overload that allows for a null value in that position would be selected. If there was a tiebreaker situation where multiple overloads match, will probably throw an error. Commented May 14 at 20:12

2 Answers 2

2

Below wins some on the pattern matching syntax, but looses some on how to pass the arguments.

switch (arguments)
{
    case [string or null, string or null]:
        return TheMethod((string?)arguments[0], (string?)arguments[1]);
    
    case [string or null]:
        return TheMethod((string?)arguments[0]);
}

The arguments[index] repeatance can be avoided by "capturing" the items in an additional and clause -- couldn't find anything in the official documentation about this.

switch (arguments)
{
    case [string or null, string or null] and [var first, var second]:
        return TheMethod(first as string, second as string);

    case [string or null] and [var first]:
        return TheMethod(first as string);
}

It seems possible to have that and clause near the corresponding pattern matching parts.

switch (arguments)
{
    case [(string or null) and var first, (string or null) and var second]:
        return TheMethod(first as string, second as string);

    case [(string or null) and var first]:
        return TheMethod(first as string);
}
Sign up to request clarification or add additional context in comments.

5 Comments

was about to post this but didn't love it somehow
arguments[0] as string surely?
Having to repeat the arguments is the only thing that irks me about this. Shortening the name could help but can hurt readability.
This feels like an improvement to me: case [string or null, string or null] and [var v1, var v2]: return TheMethod((string?)v1, (string?)v2);
Personally, I think I would leave them separated, one part to be the actual pattern, and the other to declare the variables (essentially), maybe swapping that order. Although, I could see the "combined" version useful for longer patterns (i.e., more items).
0

Take a look at this microsoft doc. It says:

You can test and convert a nullable value type to its underlying type while testing for null using the following example

int? maybe = 12;

if (maybe is int number)
{
    Console.WriteLine($"The nullable int 'maybe' has the value {number}");
}
else
{
    Console.WriteLine("The nullable int 'maybe' doesn't hold a value");
}

In the type-tests section, it says:

The declaration pattern doesn't match a null value, regardless of the compile-time type of the variable.

So you can't use nullable types directly. Instead, use the underlying type and handle the null separately.

I think based on the official documentation, you're handling it the recommended way.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.