2015-08-06 3 views
5

Предположим, что я имел в C#:неоднозначности между перегруженными методами, передаваемых в качестве делегатов в перегруженной вызов

class OverloadTest 
{ 
    void Main() 
    { 
     CallWithDelegate(SomeOverloadedMethod); 
    } 

    delegate void SomeDelegateWithoutParameters(); 
    delegate void SomeDelegateWithParameter(int n); 

    void CallWithDelegate(SomeDelegateWithoutParameters del) { } 
    void CallWithDelegate(SomeDelegateWithParameter del) { } 

    void SomeOverloadedMethod() { } 
    void SomeOverloadedMethod(int n) { } 
} 

Конечно, это не компилируется, так как линия CallWithDelegate(SomeOverloadedMethod); неоднозначно.

Теперь предположим, что существует только одна функция (без перегрузок). В этом случае не было бы никакой двусмысленности, потому что из того, что, похоже, происходит, компилятор может посмотреть тип параметра и отбросить SomeOverloadedMethod(int n) из списка кандидатов (поскольку он может принимать только SomeDelegateWithoutParameters), и поэтому он компилируется.

Я не собираюсь писать такой код; это просто из любопытства, от точки зрения автора компилятора. Я не мог найти ответа об этом, так как это довольно сложно ввести в слова.

Я хотел бы знать, есть ли какой-либо способ на C#, чтобы устранить этот вызов в Main() в приведенном примере, чтобы он скомпилировал. Как вы можете указать его так, чтобы он перешел в CallWithDelegate(SomeDelegateWithoutParameters del), пройденный SomeOverloadedMethod(), или CallWithDelegate(SomeDelegateWithParameter del) пройденный SomeOverloadedMethod(int n)?

+0

Не все, что возможно в теории, возможны в реальном C# компилятор. Это пример. Вы можете вывести, что * конечно * беззаметный был предназначен. Но компилятор не был написан для этого, возможно, для производительности или, возможно, нехватки времени/ROI, или, возможно, только никто не думал об этом. Вся вещь «делегировать» действительно очень C# -специфична и, в любом случае, рудиментарна; Я должен был бы предположить, что они будут выполнять функции refs по-разному, если бы им это нужно было сделать снова. Лучшая дорога, чтобы действительно понять это, - это прочитать спецификацию C#. –

+0

@ DaxFohl * «Конечно, беззатратный был предназначен» * => как вы можете это сказать? Вы не можете. Оба варианта одинаковы, поскольку параметр предоставляется 'CallWithDelegate'. Делегаты не являются рудиментарными, они эквивалентны типам указателей на функции, и я сомневаюсь, что они поступили бы иначе, если бы они перепроектировали язык сегодня. У вас точно такая же проблема на C++] (http://stackoverflow.com/a/2942442/3764814). –

ответ

7

Существует несколько способов устранения неоднозначности переходов групп методов.

Метод 1: бросить группу метод

CallWithDelegate((SomeDelegateWithoutParameters)SomeOverloadedMethod); 
CallWithDelegate((SomeDelegateWithParameter)SomeOverloadedMethod); 

Это устраняет неоднозначность перегрузки. Это довольно редко синтаксис в дикой природе, но он работает (C# 5 спецификация §6.6 преобразования Метод групповых):

Как и во всех других явных и неявных преобразованиях, оператор приведения можно использовать для явного выполнения способа групповое преобразование.

[...]

Метод группы могут влиять на разрешение перегрузки, а также участвовать в умозаключения типа.

Способ 2: экземпляр делегата явно

CallWithDelegate(new SomeDelegateWithoutParameters(SomeOverloadedMethod)); 
CallWithDelegate(new SomeDelegateWithParameter(SomeOverloadedMethod)); 

Это то же самое, что и предыдущий метод без синтаксического сахара. См. Спецификацию в . §7.6.10.5. Для создания более подробных выражений создания делегатов.

Обработка связывания времени из делегата-создания экспрессии формы new D(E), где D является делегат типа и E является выражением, состоит из следующих шагов:

  • Если E является группой методов, выражение создания делегата обрабатывается так же, как преобразование группы методов (§6.6) с E по D.

[...]

Там даже пример тесно связан с вашим вопросом:

Как описано выше, когда делегат создается из группы методов , формальный список параметров и тип возврата делегата определяют, какой из перегруженных методов выбрать. В примере

delegate double DoubleFunc(double x); 

class A 
{ 
    DoubleFunc f = new DoubleFunc(Square); 
    static float Square(float x) { 
     return x * x; 
    } 
    static double Square(double x) { 
     return x * x; 
    } 
} 

A.f поля инициализируются с делегатом, который относится ко второму Square методы, так как этот метод точно соответствует формальному списку параметров и типа возвращаемого DoubleFunc. Если бы второй метод Square не присутствовал, произошла ошибка времени компиляции.

Способ 3: использовать лямбда

CallWithDelegate(() => SomeOverloadedMethod()); 
CallWithDelegate(i => SomeOverloadedMethod(i)); 
CallWithDelegate((int i) => SomeOverloadedMethod(i)); // Explicit types, if needed 

Эта форма не является неоднозначным, но она имеет косвенность (лямбда называется, и затем вызывает целевой метод). Это может быть оптимизировано JIT, хотя это, вероятно, не будет иметь видимого влияния на производительность.

Метод 4: использовать анонимные делегаты

CallWithDelegate(delegate() { SomeOverloadedMethod(); }); 
CallWithDelegate(delegate(int i) { SomeOverloadedMethod(i); }); 

Это эквивалентно лямбда вызовов, но он использует громоздкий (и старше) delegate синтаксис.


Если вы хотите знать правила точных разрешений перегрузки, они описаны в спецификации в разрешения §7.5.3 перегрузки.

+0

Да! Методы 1 и 2 именно то, что я хотел знать. – HLorenzi

0

Хотя я не эксперт по компилятору, могу вам сказать, что, поскольку вы поставили 2 перегруженных метода, которые соответствуют обоим методам, компилятор не имеет способа определить ваше фактическое намерение. Он не может скомпилироваться, потому что во время компиляции в настоящее время нет фактической идентифицирующей информации, как уже упоминалось Лукасом, которую вы можете использовать, чтобы устранить двусмысленность. Для компилятора для решения этой задачи потребуется информация о времени выполнения, которую вы действительно хотели использовать на основе аргументов, которые вы можете попробовать и пройти.

 Смежные вопросы

  • Нет связанных вопросов^_^