Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# 8 switch expression: No best type was found for the switch expression

I have added a code in my startup class (.net core 3.1) to return the type based on parameter and I get compile-time errors.

I have created a running example in sharplab. if switch expression contains the string or other objects it runs fine.

working example 1:

var x = key switch
            {
                "myhandler1" => "something",
                "myhandler2" => "something else",
                _ => "default case"
            };

working example 2:

object obj =  s switch {
            "a" => new object(),
            "b" => new DateTime(),
            _ => throw new NotImplementedException()
        };

Error example:

interface IHandler { }
public class BaseHandler { }
public class MyHandler1: BaseHandler, IHandler { }
public class MyHandler2: BaseHandler, IHandler { }

class Program
{
    static void Main(string[] args)
    {

        var key = "myhandler1";

        var handler = key switch
        {
            "myhandler1" => new MyHandler1(),
            "myhandler2" => new MyHandler2(),
            _ => throw new NotImplementedException()
        };

        var x = key switch
        {
            "myhandler1" => "something",
            "myhandler2" => "something else",
            _ => "default case"
        };

        Console.WriteLine("Hello World!");
    }
}

original problem (needs fixing):

serviceCollection.AddTransient<Func<string, IHandler>>(sp => key =>
            {
                return key switch
                {
                    Constants.Brand => sp.GetService<Handler1>(),
                    Constants.Series => sp.GetService<Handler2>(),
                    _ => throw new NotImplementedException()

                };
}

found this link: https://github.com/dotnet/csharplang/issues/2728

Thanks to Pavel and Marc, below is the fix:

serviceCollection.AddTransient<Func<string, IHandler>>(sp => key =>
            {
                return key switch
                {
                    Constants.Brand => (sp.GetService<Handler1>() as IHandler),
                    Constants.Series => (sp.GetService<Handler2>() as IHandler),
                    _ => throw new NotImplementedException()

                };
}
like image 660
Kamran Pervaiz Avatar asked Apr 02 '20 13:04

Kamran Pervaiz


People also ask

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

What do you mean by C?

" " C is a computer programming language. That means that you can use C to create lists of instructions for a computer to follow. C is one of thousands of programming languages currently in use.

What is C language used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

Why is C named so?

Quote from wikipedia: "A successor to the programming language B, C was originally developed at Bell Labs by Dennis Ritchie between 1972 and 1973 to construct utilities running on Unix." The creators want that everyone "see" his language. So he named it "C".


3 Answers

You should explicitly declare a type of handler, instead of var

IHandler handler = key switch //or BaseHandler handler = key switch
{
    "myhandler1" => new MyHandler1(),
    "myhandler2" => new MyHandler2(),
    _ => throw new NotImplementedException()
};

In your sharplab sample both handlers implement IHandler interface and inherit BaseHandler class, compiler simply doesn't know which type to use, you should tell it him explicitly

interface IHandler { }
public class BaseHandler { }
public class MyHandler1 : BaseHandler, IHandler { }
public class MyHandler2 : BaseHandler, IHandler { }

The same is true for the dependency injection sample, you should explicitly declare a type (assuming that Handler1 and Handler2 implement IHandler)

return key switch
{
    Constants.Brand => sp.GetService<Handler1>(),
    Constants.Series => (IHandler) sp.GetService<Handler2>(),
    _ => throw new NotImplementedException()
};

You can do it only for one constant, compiler is smart enough to do the rest of job for you

like image 103
Pavel Anikhouski Avatar answered Oct 13 '22 21:10

Pavel Anikhouski


You're dealing with a covariance issue. You've specified that the return type of the Func should be IHandler, but this type param is invariant. As such, you have to actually return IHandler, not just a class that implements it.

serviceCollection.AddTransient<Func<string, IHandler>>(sp => key =>
{
    return key switch
    {
        Constants.Brand => (IHandler)sp.GetService<Handler1>(),
        Constants.Series => (IHandler)sp.GetService<Handler2>(),
        _ => throw new NotImplementedException()
    };
}
like image 25
Chris Pratt Avatar answered Oct 13 '22 19:10

Chris Pratt


var is fussy - it wants things unambiguous, and it isn't obvious what type handler should be here since MyHandler1 and MyHandler2 are different types; basically, pick some common base type or implemented interface from MyHandler1 and MyHandler2, and use that instead of var. At worst case, object should suffice:

object handler = key switch
{
    "myhandler1" => new MyHandler1(),
    "myhandler2" => new MyHandler2(),
    _ => throw new NotImplementedException()
};

The compiler won't attempt to do this itself.

like image 1
Marc Gravell Avatar answered Oct 13 '22 20:10

Marc Gravell