Разрешение перегрузки метода и универсальные / контравариантные интерфейсы в C # - PullRequest
7 голосов
/ 02 марта 2012

Я думаю, что мою проблему лучше всего объяснить с помощью фрагмента кода моей иерархии классов / интерфейсов:

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?

EDIT Просто чтобы вы знали: в настоящее время я использую Silverlight 4, но я думаю, что это общее поведение C #.

Ответы [ 3 ]

2 голосов
/ 02 марта 2012

Ваш класс Selector реализует интерфейс ITransform, что означает, что вам придется включать код для обработки Transform (ISelection). Ваш класс также может обрабатывать Transform (IValue), но только если он унаследован от класса Editor.

Причина, по которой он выбирает вариант ISelection, заключается в том, что это вариант, который явно объявлен в вашем классе Selector. Чтобы выбрать Transform (IValue), компилятор должен сделать предположение, что вы скорее обрабатываете вызов из своего базового класса (редактор).

Редактировать: Некоторый фон из C # spec.

Каждый из этих контекстов определяет набор функций-кандидатов и список аргументов по-своему уникальным образом, как описано в подробно в разделах, перечисленных выше. Например, набор Кандидаты на вызов метода не включают помеченные методы переопределение (§7.4), и методы в базовом классе не являются кандидатами, если таковые имеются применим метод в производном классе (§7.6.5.1).

0 голосов
/ 02 марта 2012

Вопрос 1: Почему s1.Transform (v) разрешается в ITransform<ISelection>, а не в ITransform<IValue>, как во втором случае?

Для меня это разрешается до Selector.Transform<ISelection>.Так и должно быть: вы сказали, что это был Selector, и у Selector есть открытый метод с именем Transform, и он принимает ISelection.IValue расширяет выбор IS.Когда это будет приведено в 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>

        }

      }
0 голосов
/ 02 марта 2012

В Q1 я думаю, что это потому, что компилятор будет искать более короткую цепочку иерархии, чтобы получить допустимую перегрузку.Чтобы получить ITransform на S1, вам нужно пойти дальше.

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

Я поищу источник для проверки.

...