[英]Method overload resolution and generic/contravariant interfaces in C#
我认为我的问题最好用我的类/接口层次结构的代码片段来解释:
public interface ITransform<D> // or <in D> --> seems to make no difference here
{
void Transform(D data);
}
interface ISelection {}
interface IValue : ISelection {}
public interface IEditor : ITransform<IValue> {}
public interface ISelector : IEditor, ITransform<ISelection> {}
class Value : IValue { ... }
class Editor : IEditor { ... } // implements ITransform<IValue>
class Selector : Editor, ISelector { ... } // implements ITransform<ISelection>
Value v = new Value();
Selector s1 = new Selector();
ISelector s2 = s1;
s1.Transform(v); // resolves to ITransform<ISelection> --> WHY?
s2.Transform(v); // resolves to ITransform<IValue> --> OK
问题1:为什么s1.Transform(v)
解析为ITransform<ISelection>
而不是像第二种情况那样解决ITransform<IValue>
?
问题2:对于问题1,如果ITransform
是<D>
或<in D>
,似乎没有区别。 但是你在我的类/接口层次结构中使用<in D>
是否看到任何其他问题? 由于ISelector
实现了ITransform<IValue>
和ITransform<ISelection>
我有点怀疑。 由于IValue
继承了ISelection
因此可能会出现IValue
问题吗?
编辑只是为了让你知道:我目前正在使用Silverlight 4,但我认为这是一般的C#行为。
您的Selector类实现了ITransform接口,这意味着您必须包含处理Transform(ISelection)的代码。 您的类也可以处理Transform(IValue),但仅限于Editor类中的继承方法。
它选择ISelection变体的原因是因为这是在Selector类中显式声明的变体。 要选择Transform(IValue),编译器必须假设您更愿意处理来自基类(编辑器)的调用。
编辑:C#规范的一些背景知识。
这些上下文中的每一个都以其自己独特的方式定义候选函数成员集和参数列表,如上面列出的部分中详细描述的。 例如,方法调用的候选集不包括标记为override的方法(第7.4节), 如果派生类中的任何方法适用(第7.6.5.1节),则基类中的方法不是候选方法。
在Q1,我认为它是因为编译器将寻找较短的层次结构链来获得有效的重载。 要在S1上获得ITransform,您将不得不走得更远。
s1->Selector->ISelector->ITransform<Selector>
s1->Selector->Editor->IEditor->ITransform<IValue>
s1->Selector->ISelector->IEditor->ITransform<IValue>
我会找一个来源验证。
问题1:为什么s1.Transform(v)解析为
ITransform<ISelection>
而不是像第二种情况那样解决ITransform<IValue>
?
对我来说,这解析为Selector.Transform<ISelection>
。 它应该:你说它是一个Selector,而Selector有一个名为Transform的公共方法,它需要一个ISelection。 IValue扩展了ISelection。 它何时会被强制转换为ITransform? 我不相信这说明任何逆转,我认为这是隐含的转换。
问题2:对于问题1,如果ITransform
in
或不变,则似乎没有任何区别
因为你使用泛型参数的方法不精氨酸返回类型,规则规定,该参数必须是contravariantly有效的,这将允许in
,并禁止out
。
public class Example
{
public interface ITransform<D> // or <in D> --> seems to make no difference here
{
void Transform(D data); //contravariant in ITranform<out D>.
//D Transform(string input); //covariance ok
}
public interface ISelection { }
public interface IValue : ISelection { }
public interface IEditor : ITransform<IValue> { }
public interface ISelector : IEditor, ITransform<ISelection>
{
new void Transform(ISelection data);
}
class Value : IValue { }
class Editor : IEditor
{
public void Transform(IValue data)
{
throw new NotImplementedException();
}
}
class Foo : Editor, ISelector
{
public void Transform(ISelection data)
{
throw new NotImplementedException();
}
}
public void Whatever()
{
Value v = new Value();
Foo s1 = new Foo();
IEditor s2 = s1;
s1.Transform(v); // resolves to Foo.Tranform(ISelection)
s2.Transform(v); // resolves to ITransform<IValue> --> cast into IEditor, which sig says ITransform<IValue>
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.