【发布时间】:2013-03-29 04:22:18
【问题描述】:
例如,让我们使用计算器之类的东西,其中包含各种类型的元素、评估不同元素类型的函数以及存储元素和运行函数的上下文。接口是这样的:
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 和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 的所有调用进行硬输入?在现实世界中我有几个功能,所以能够避免它会很棒。
Bounty 可以很好地解释为什么添加 <double> 有助于编译器。这是编译器懒得说的问题吗?
旧更新:使用委托
一个选项可以是在IContext.Evaluate 中使用代表而不是IFunctions:
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]);
}
}
这样做,我们在调用IContext.Evaluate 时不需要硬输入<double>:
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 对象本身这一事实。
【问题讨论】:
标签: c# generics inheritance type-inference