Home > database >  Can I add compile time checks that this is a legal handler function? (constrained generic)
Can I add compile time checks that this is a legal handler function? (constrained generic)

Time:03-28

See comments... I want the 2nd MapPost call to fail at compile time. I'm just experimenting with asp.net minimal pipeline. I think this requires constrained generics but I'm new to C# so thought someone could quickly answer this one (hopefully!).

var router = new ExampleRouter();
var controller = new ExampleWebController();

router.MapPost("/api/ProcessJob", controller.ProcessJob);

// I would like this to be a compile time error, instead of a runtime startup error.
router.MapPost("/api/ProcessJob2", controller.InvalidHandler);

class ExampleWebController {
    public ExampleWebController() { }
    public void InvalidHandler() {  }
    public bool ProcessJob(Job job) { return true; }
}

class ExampleRouter {
    public ExampleRouter() { }

    // TODO: Check at compile that `handler` takes a DTO and returns bool
    public void MapPost<TFunc>(string path, TFunc handler) {
        var func_type = typeof(TFunc);
        // Prints "System.Func" for ProcessJob
        // Prints "System.Action" for InvalidHandler
        Console.WriteLine(func_type);
        var args = func_type.GetGenericArguments();
        foreach (Type arg in args) {
            // Prints "Job", "System.Boolean" for ProcessJob...awesome
            Console.WriteLine(arg);
        }

        // Construct the DTO dynamically.
        // In reality, we'd do this when a http request comes in.
        var job_type = args[0];
        var job_obj = Activator.CreateInstance(job_type);
        DTO dto = (DTO)job_obj;
        // Prints "Hello I constructing from test body"
        dto.construct_from_body("test body");
    }
}

interface DTO {
    public void construct_from_body(string body);
}

class Job : DTO
{
    public Job() {  }

    public void construct_from_body(string body)
    {
        Console.WriteLine($"Hello I constructing from {body}");
    }
}

CodePudding user response:

What is invalid? Do you want to make the compiler fail with every void handler?
One way to achieve this up to two parameters:

public class MapPostImplementations
{
    struct Discard
    {
    }

    public void MapPost<A, B, R>(string path, Func<A, B, R> handler)
    {
        // Ignore the Discard type arguments
    }

    public void MapPost<A, R>(string path, Func<A, R> handler)
    => MapPost<A, Discard, R>(path, (a, _) => handler(a));

    public void MapPost<R>(string path, Func<R> handler)
    => MapPost<Discard, R>(path, _ => handler());

    class ExampleWebController
    {
        public void InvalidHandler() { }
        public bool ProcessNumber(int v) { return true; }
        public bool ProcessString(string v) { return true; }
    }

    ExampleWebController controller = new ExampleWebController();

    void Test()
    {
        MapPost<string, bool>("/api/foo", controller.ProcessString);

        MapPost<int, bool>("/api/numberFoo", controller.ProcessNumber);

        // does not compile
        //MapPost("/api/bar", controller.InvalidHandler);
    }
}

CodePudding user response:

As of 2022, it appears I can only check that it is "some kind of delegate" in the where constraint :-(

   public void MapPost<TFunc>(string path, TFunc handler) where TFunc: Delegate
  • Related