C#中的方法重载解析和泛型/逆变接口

7

我认为最好通过一个代码片段来解释我的问题,代码如下所示:

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>似乎没有区别。但您是否在使用我的类/接口层次结构中看到其他问题?我有些怀疑,因为ISelector实现ITransform<IValue>ITransform<ISelection>。由于IValue继承ISelection,可能会导致逆变引起任何问题吗?

编辑 只是想让您知道:我目前正在使用Silverlight 4,但我认为这是一般C#的行为。


当我将D声明为逆变(in)时,两者都解析为ITransform<ISelection>。 - Julien
就像我说的那样,ITransform是否是逆变似乎完全没有影响。对于s1,这两个调用始终解析为ITransform<ISelection>。 - Stephan
我不确定我们是否彼此理解...你的"For s1 both calls"是什么意思?当ITransform是反变时,我有一个区别:s1.Transform(v)和s2.Transform(v)都解析为ITransform<ISelection>。 - Julien
算了吧。"对于s1的两个调用"没有意义。至于你的"差异":那正是我的问题所在。我以为s1.Transform(v)会解析为ITransform<IValue>,但它解析为ITransform<ISelection>。而这里ITransform是否逆变并没有区别。 - Stephan
好的,我明白了!希望一些C#大牛能给我们一个答案,我真的很好奇这个问题的答案... - Julien
3个回答

2
您的Selector类实现了ITransform接口,这意味着您需要包含处理Transform(ISelection)的代码。您的类也可以处理Transform(IValue),但仅限于从Editor类继承的方法。
选择ISelection变体的原因是因为它在您的Selector类中明确声明。要选择Transform(IValue),编译器必须假定您更喜欢从基类(Editor)处理调用。
编辑:C#规范的一些背景信息。
每个上下文都以自己独特的方式定义候选函数成员集和参数列表,如上面列出的详细信息所述。例如,方法调用的候选集不包括标记为override (§7.4)的方法,并且如果派生类中的任何方法适用,则基类中的方法不是候选项 (§7.6.5.1)。

好的,我明白了,这回答了我的问题,如果我在选择器中重写ITransform<IValue>,它甚至不会被调用。无论如何都很奇怪。谢谢maka。 - Stephan

0

关于Q1,我认为这是因为编译器会寻找更短的层次链来获取有效的重载。要在S1上获得ITransform,您需要继续深入。

s1->Selector->ISelector->ITransform<Selector>
s1->Selector->Editor->IEditor->ITransform<IValue>
s1->Selector->ISelector->IEditor->ITransform<IValue>

我会寻找一个来源来进行验证。


1
嗯,我也考虑过这个问题。但这真的有意义吗?这将打破我的观点,“在方法重载中,编译器总是解析到最具体类型的重载”。能够看到一些验证这一点的来源将是很好的。提前感谢。 - Stephan
我明白了,我会在不同的答案中发布。 - maka

0
问题1:为什么s1.Transform(v)解析为ITransform<ISelection>而不是第二种情况下的ITransform<IValue>
对我来说,这会解析为Selector.Transform<ISelection>。这也应该是这样的:你说它是一个Selector,Selector有一个名为Transform的公共方法,它需要一个ISelection。IValue扩展了ISelection。它何时被强制转换为ITransform?我不认为这说明任何逆变性,我认为这是隐式转换。
问题2:对于问题1,如果ITransform是in或不变的,似乎没有区别
由于您将泛型参数用作方法参数而不是返回类型,规则规定参数必须是逆变有效的,这将允许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>

        }

      }

嗨,sweaver,说实话,我不确定你想告诉我什么。但是,你的代码给了我在Selector中执行以下操作的想法:public new void Transform(IValue value) { base.Transform(value); }。现在,当直接访问Selector而不是通过ISelector时,将调用“正确”的代码(为什么?请参见maka的答案)。谢谢! - Stephan
我只是在说我无法复制“//解析为ITransform<ISelection>” - s1对象是一个选择器,从未成为ITransform。 - Scott Weaver
是的,没错。但我仍然期望Selector.Transform(value)解析为ITransform<Value>而不是ITransform<Selection>(两者都由Selector实现),因为Value比Selection更具体。然而,正如C#规范中所述(请参见maka的答案),这被证明是一个错误的假设。无论如何,还是谢谢。 - Stephan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接