我正在编写一个方法,给定输入和提取器函数处理输入并返回结果。在这个简化的示例中,如果结果为 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.
我找到了这些有效的调用方法:
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?>
?
为不可空返回添加重载是不可能的,因为我们已经有一个与它冲突的引用类型的重载:
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);
}
将带有
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.
删除除一个之外的所有重载,并删除其通用约束也不起作用。
.NET 小提琴:https://dotnetfiddle.net/1TRawz
如果您感觉受到 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
};
}
}