例如,让我们使用计算器之类的东西,其中包含各种类型的元素、评估不同元素类型的函数以及存储元素和运行函数的上下文。接口是这样的:
public interface IElement {
}
public interface IChildElement : IElement {
double Score { get; }
}
public interface IGrandchildElement : IChildElement {
int Rank { get; }
}
public interface IFunction<Tout, in Tin> where Tin : IElement {
Tout Evaluate(Tin x, Tin y);
}
public interface IContext<Tin> where Tin : IElement {
Tout Evaluate<Tout>(string x, string y, IFunction<Tout, Tin> eval);
}
请注意,函数可能返回任意类型。一个虚拟实现如下,我有一个调用的函数Foo
,可以同时用于IChildElement
and IGrandchildElement
,并double
在两种情况下都返回:
public class ChildElement : IChildElement {
public double Score { get; internal set; }
}
public class GrandchildElement : ChildElement, IGrandchildElement {
public int Rank { get; internal set; }
}
public class Foo : IFunction<double, IChildElement>, IFunction<double, IGrandchildElement> {
public double Evaluate(IChildElement x, IChildElement y) {
return x.Score / y.Score;
}
public double Evaluate(IGrandchildElement x, IGrandchildElement y) {
return x.Score * x.Rank / y.Score / y.Rank;
}
}
public class Context<T> : IContext<T> where T : IElement {
protected Dictionary<string, T> Results { get; set; }
public Context() {
this.Results = new Dictionary<string, T>();
}
public void AddElement(string key, T e) {
this.Results[key] = e;
}
public Tout Evaluate<Tout>(string x, string y, IFunction<Tout, T> eval) {
return eval.Evaluate(this.Results[x], this.Results[y]);
}
}
一些示例执行:
Context<IChildElement> cont = new Context<IChildElement>();
cont.AddElement("x", new ChildElement() { Score = 1.0 });
cont.AddElement("y", new ChildElement() { Score = 2.0 });
Foo f = new Foo();
double res1 = cont.Evaluate("x", "y", f); // This does not compile
double res2 = cont.Evaluate<double>("x", "y", f); // This does
如您所见,我的问题是我似乎需要硬输入对Context.Evaluate
. 如果我不这样做,编译器会说它无法推断参数的类型。这对我来说特别引人注目,因为在这两种情况下,Foo
函数都会返回double
。
如果Foo
只实现IFunction<double, IChildElement>
或IFunction<double, IGrandchildElement>
我没有这个问题。但确实如此。
我不明白。我的意思是,添加<double>
不会区分IFunction<double, IGrandchildElement>
和IFunction<double, IChildElement>
因为它们都返回double
。据我了解,它没有为编译器提供任何额外的信息来区分。
在任何情况下,有什么方法可以避免必须对所有调用进行硬输入Task.Evaluate
?在现实世界中我有几个功能,所以能够避免它会很棒。
赏金来解释为什么添加<double>
对编译器有帮助。这是编译器懒得说的问题吗?
旧更新:使用委托
一个选项可能是使用委托而不是IFunction
s in IContext.Evaluate
:
public interface IContext<Tin> where Tin : IElement {
Tout Evaluate<Tout>(string x, string y, Func<Tin, Tin, Tout> eval);
}
public class Context<T> : IContext<T> where T : IElement {
// ...
public Tout Evaluate<Tout>(string x, string y, Func<T, T, Tout> eval) {
return eval(this.Results[x], this.Results[y]);
}
}
<double>
这样做,我们在调用时不需要硬输入IContext.Evaluate
:
Foo f = new Foo();
double res1 = cont.Evaluate("x", "y", f.Evaluate); // This does compile now
double res2 = cont.Evaluate<double>("x", "y", f.Evaluate); // This still compiles
所以这里编译器确实按预期工作。我们避免了硬类型的需要,但我不喜欢我们使用IFunction.Evaluate
而不是IFunction
对象本身的事实。