C# 可以隐式转换泛型函数的委托参数吗?

问题描述 投票:0回答:1

我正在编写一个方法,给定输入和提取器函数处理输入并返回结果。在这个简化的示例中,如果结果为 null,它只会抛出异常:

public static TResult Process<TInput, TResult>(TInput input, Func<TInput, TResult?> processor)
    where TResult : struct
{
    return processor(input) ?? throw new Exception();
}

// for later do note that this overload also exist:
public static TResult Process<TInput, TResult>(TInput input, Func<TInput, TResult?> processor)
    where TResult : class
{
    return processor(input) ?? throw new Exception();
}

不能这样调用:

bool c = Process(false, x => !x); // Error: The type arguments for method 'Program.Process<TResult>(bool, Func<bool, TResult?>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

想法1:

我找到了这些有效的调用方法:

Func<bool, bool?> processor = x => !x;
bool a = Process(false, processor);

bool b = Process<bool, bool>(false, x => !x);

但是这些太冗长了(真正方法的通用参数通常非常复杂)。有没有办法让 C# 选择正确的泛型参数并将

Func<bool, bool>
隐式转换为
Func<bool, bool?>

想法2:

为不可空返回添加重载是不可能的,因为我们已经有一个与它冲突的引用类型的重载:

public static TResult Process<TInput, TResult>(TInput input, Func<TInput, TResult> processor) // Error: Type 'Program' already defines a member called 'Process' with the same parameter types
    where TResult : struct
{
    return processor(input);
}

想法3:

将带有

class
通用约束的重载转换为
notnull
通用约束。这修复了原来的调用,但破坏了这个:

bool e = Process(false, x => x ? x : null); // Error: The type arguments for method 'Program.Process<TInput, TResult>(TInput, Func<TInput, TResult?>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

想法4:

删除除一个之外的所有重载,并删除其通用约束也不起作用。


.NET 小提琴:https://dotnetfiddle.net/1TRawz

c# generics
1个回答
0
投票

如果您感觉受到 C# 中的泛型(大多数库如 LINQ、FluentAssertions 等都使用)的限制,您可以完全流畅地使用:

public class Program
{
    public static void Main()
    {
        bool c = Process.Object(false).Using(x=> !x);
        //or even chain them
        var garbage = Process
            .Object(false)
            .Using(x => !x)
            .Next(x => x ? "a" : "b")
            .Next(x => x[0])
            .Next(x => x & 8);
    }
}

public class ProcessorInput<TIn>
{
    public Func<TIn> InputProvider { get; set; }
}

public class Processor<TIn, TOut>
{
    public Func<TIn> InputProvider { get; set; }
    public Func<TIn, TOut> OutputProvider { get; set; }
    
    public static implicit operator TOut(Processor<TIn, TOut> processor)
    {
        return processor.OutputProvider(processor.InputProvider());
    }

    public TOut Eval()
    {
        return OutputProvider(InputProvider());
    }
}

public static class Process
{
    public static ProcessorInput<TIn> Object<TIn>(TIn input)
    {
        return Object(() => input);
    }
    public static ProcessorInput<TIn> Object<TIn>(Func<TIn> inputProvider)
    {
        return new ProcessorInput<TIn>() { InputProvider = inputProvider };
    }
    public static Processor<TIn, TOut> Using<TIn, TOut>(this ProcessorInput<TIn> processorInput, Func<TIn, TOut> outputProvider)
    {
        return new Processor<TIn, TOut>()
            { InputProvider = processorInput.InputProvider, OutputProvider = outputProvider };
    }
    public static Processor<TOut, TNextOut> Next<TIn, TOut, TNextOut>(this Processor<TIn, TOut> processor, Func<TOut, TNextOut> nextOutputProvider)
    {
        return new Processor<TOut, TNextOut>
        {
            InputProvider = processor.Eval,
            OutputProvider = nextOutputProvider
        };
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.