Is it possible in C# to declare a delegate type in which the input parameters are dynamic but the return type is not?

55 Views Asked by At

I am in .NET 6.0. I have a class, we'll call him "ComplexObject". ComplexObject has some complex/dynamic data structures under the hood [that aren't relevant to the question], and the best way we found to consume some of this data from a different class is with a caller-specified Delegate. So the ComplexObject class has a method that currently looks like this (simplified for clarity):

public T Evaluate<T>(Delegate method)
{
    var parameters = DoMagicToGetTheParameterValues(method);
    var obj = method.DynamicInvoke(parameters);
    return CustomConvertTo<T>(obj);
}

This method gives the caller the ability to do something like this:

{
    var instance = GetComplexObject();
    var name = instance.Evaluate<string>((SimpleEntity simple) => {
        return simple.Name ?? simple.ToString();
    });
    var result = SomeMethodThatNeedsANameParameter(name);
    return result;
}

This code works as intended, but because of the vanilla Delegate argument type, it cannot infer the return type, enforce a return statement in general, or receive any benefits that generics give you in .NET.

Is there a way in C# to create a delegate type that enforces a specified result type but does not restrict the input arguments?

TLDR; is this possible in .NET?

public Delegate T EvaluationDelegate<T>(/* ANY number of parameters of ANY type in ANY order from ANY caller */);

I tried a bunch of different combinations of delegate declarations and none of them worked. I know there is a solution that looks something like this:

public TResult Evaluate<TArg1, TResult>(Func<TArg1, TResult> method);
public TResult Evaluate<TArg1, TArg2, TResult>(Func<TArg1, TArg2, TResult> method);
public TResult Evaluate<TArg1, TArg2, TArg3, TResult>(Func<TArg1, TArg2, TArg3, TResult> method);
...etc, keep going until I deem necessary

But even then, there's an issue in .NET 6. The syntax in the caller, where the input arguments are specified this way:

var name = instance.Evaluate<string>((SimpleEntity simple) => {
    return simple.Name ?? simple.ToString();
});

var name = instance.Evaluate<string>((SimpleEntity simple, MediumEntity medium) => {
    return simple.Name ?? medium.Name ?? simple.ToString();
});

Anything beyond one input argument seems to run into the incompatible anonymous function signature when the method names match, and the Type arguments cannot be inferred from the usage error when the method names differ (I would rather them match). At that point, I'd rather just trust the caller then force them to actually type out the generic arguments...

This may be this is possible in .NET 7.0 or .NET 8.0, I have not tried in either of those versions of .NET. Nonetheless, the focus of the question is above - if it possible to accomplish this goal with a delegate type.

Thank you in advance for anyone who took the time to read/respond to my question!

0

There are 0 best solutions below