C# interface concept clarification with IActionResult in ASP.NET Core

Viewed 1514

I'm trying to understand C#, went through the basics and started trying things in ASP.NET Core but couldn't get my head around the interface part.

I understood that an interface is like a contract of what the class should contain and the interface method doesn't have a body, like this for example:

interface IAnimal 
{
    void animalSound(); 
}

and then we provide the implementation in the class, like this:

class Pig : IAnimal 
{
    public void animalSound() 
    {
        Console.WriteLine("The pig says: wee wee");
    }
}

However, when I hit the real world (as usual) things are different :)

In ASP.NET Core we can use the IActionResult interface to help us with responses - to return Ok(), NotFound(), etc.

Like in this example

namespace CityInfo1.API.Controllers
{
    [ApiController]
    [Route("api/cities")]
    public class CitiesControllers : ControllerBase
    {
        [HttpGet]
        public IActionResult GetCities()
        {
            return Ok(CitiesDataStore.Current.Cities);
        }

        [HttpGet("{id}")]
        public IActionResult GetCity(int id)
        {
            var cityToReturn = CitiesDataStore.Current.Cities
                .FirstOrDefault(c => c.Id == id);

            if (cityToReturn == null)
            {
                return NotFound();
            }

            return Ok(cityToReturn);
        }
    }
}

But here, IActionResult is actually providing the functionality and implementation of different methods.

Another question, in the IAnimal example, is it mandatory to use the AnimalSound() in the classes that uses this interface? And if so, is it possible to make it optional?

2 Answers

IActionResult is declared like this:

public interface IActionResult
{
    Task ExecuteResultAsync(ActionContext context);
}

Very similar to your IAnimal, isn't it?

There is also a class called OkObjectResult that implements IActionResult, which has this ExecuteResultAsync method.

public class OkObjectResult : IActionResult {
    public Task ExecuteResultAsync(ActionContext context)
    {
        // ...
    }
}

OkObjectResult doesn't actually implement IActionResult directly. It inherits from ObjectResult, which inherits from ActionResult, which implements IActionResult. The code is for illustration purposes only.

This corresponds to the Pig class in your non-real-world example.

Now which part of your non-real-world example does your CitiesController code correspond to? Well, you missed that part out in your non-real-world example. If we were to continue it, we could say there is a AnimalFactory class that produces IAnimals:

public class AnimalFactory {
    public IAnimal GetAnimal() {
        return new Pig();
    }
}

For the sake of argument, let's say that the users of AnimalFactory don't care what kind of animal they get, as long as it is an IAnimal - i.e. something that has the animalSounds method. We, as the implementers, is free to decide what kind of animal to return, and in this case, I decided to always return a pig.

Going back to your CitiesController, you are supposed to return an IActionResult in GetCity, but the caller doesn't care which specific kind of IActionResult, as long as it is an IActionResult - has the ExecuteResultAsync(ActionContext) method. You've decided to return whatever is return by Ok (which is a method that CitiesController inherit from BaseController). And FYI, OK returns an instance of OkObjectResult. Now you know why I mentioned that at the beginning!

is it mandatory to declare the AnimalSound() method in the classes that implement the interface?

Yes, because by implementing the interface, you are saying that the class has all the members that the interface requires. Declaring that your class implements an interface, then not providing the required members, is just lying, isn't it? :D

IActionResult type

The IActionResult return type is appropriate when multiple ActionResult return types are possible in an action. The ActionResult types represent various HTTP status codes. Any non-abstract class deriving from ActionResult qualifies as a valid return type. Some common return types in this category are BadRequestResult (400), NotFoundResult (404), and OkObjectResult (200). Alternatively, convenience methods in the ControllerBase class can be used to return ActionResult types from an action. For example, return BadRequest(); is a shorthand form of return new BadRequestResult();.

Because there are multiple return types and paths in this type of action, liberal use of the [ProducesResponseType] attribute is necessary. This attribute produces more descriptive response details for web API help pages generated by tools like Swagger. [ProducesResponseType] indicates the known types and HTTP status codes to be returned by the action.

Related