2009-04-05 4 views
30

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

void Method(TypeA a) { } 
void Method(TypeB b) { } 

Я называю этот метод с:

Method(null); 

Какая перегрузка метода называется? Что я могу сделать, чтобы обеспечить определенную перегрузку?

+0

Еще один, чтобы быть в курсе перегружен методы, где по крайней мере один метод использует Params ключевое слово. –

ответ

67

Это зависит от TypeA и TypeB.

  • Если только один из них применим (например, не существует переход от null к TypeB, потому что это тип значения, но TypeA является ссылочным типом), то вызов будет выполнен к соответствующему одному.
  • В противном случае это зависит от отношений между TypeA и TypeB.
    • Если существует неявное преобразование из TypeA в TypeB, но не неявное преобразование из TypeB в TypeA будет использоваться перегрузка с помощью TypeA.
    • Если существует неявное преобразование из TypeB в TypeA, но не неявное преобразование из TypeA в TypeB будет использоваться перегрузка с помощью TypeB.
    • В противном случае вызов неоднозначен и не будет скомпилирован.

можно найти в разделе 7.4.3.4 в спецификации C# 3.0 для подробных правил.

Вот пример того, что это не двусмысленно. Здесь TypeB происходит от TypeA, что означает неявное преобразование от TypeB до TypeA, но не наоборот. При этом используется перегрузка с помощью TypeB:

using System; 

class TypeA {} 
class TypeB : TypeA {} 

class Program 
{ 
    static void Foo(TypeA x) 
    { 
     Console.WriteLine("Foo(TypeA)"); 
    } 

    static void Foo(TypeB x) 
    { 
     Console.WriteLine("Foo(TypeB)"); 
    } 

    static void Main() 
    { 
     Foo(null); // Prints Foo(TypeB) 
    } 
} 

В общем, даже перед лицом иначе-неоднозначного вызова, чтобы гарантировать, что используется определенная перегрузка, просто привести:

Foo((TypeA) null); 

или

Foo((TypeB) null); 

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

+0

@Jon: Почему поведение, описанное в случае, когда доступно неявное преобразование? Я бы предпочел бы обратное. Это просто вопрос определения? –

+2

@divo: Это определенно дизайнерское решение, но, вероятно, причина этого в том, что метод TypeB является в значительной степени специализированным случаем метода TypeA. Я имею в виду, что для каждого допустимого аргумента arg по умолчанию TypeA будет сопоставляться, если объект не известен как TypeB. Такое поведение для null обеспечивает такую ​​согласованность. –

+0

Mehrdad прав - если вы можете конвертировать из TypeB в TypeA, тогда TypeB более специфичен, чем TypeA, поэтому он будет выбран в предпочтении. –

1

двусмысленный звонок. (ошибка времени компиляции).

+0

привет, Я пробовал следующий образец и не получил ошибку компиляции; любые мысли почему? класс Программа { void test1 (ICollection a) { Console.WriteLine ("test1-ICollection"); } void test1 (IList a) { Console.WriteLine ("test1-List"); } static void Main (string [] args) { Программа p = новая программа(); p.test1 (null); Console.ReadKey(); }} –

+0

но я получил ошибку компиляции, когда я изменил один из перегруженных принимать параметр типа IComparer класс Программа { аннулируются test1 (IList а) { Console.WriteLine ("test1- Список"); } void test1 (IComparer a) { Консоль.ЕЫпе ("test1-IComparer"); } static void Main (string [] args) { Программа p = новая программа(); p.test1 (null); Console.ReadKey(); } } –

8

Jon Skeet дал исчерпывающий ответ, но с точки зрения дизайна вы не должны зависеть от угловых случаев спецификации компилятора. Если ничего другого, если вам нужно посмотреть, что он делает, прежде чем писать, следующий человек, который попытается прочитать его, не будет знать, что он делает. Это не поддерживается.

Перегрузки есть для удобства, а две разные перегрузки с одинаковым именем должны делать то же самое. Если эти два метода делают разные вещи, переименуйте один или оба из них.

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

например. string ToString(string format, System.IFormatProvider provider) имеет большинство параметров,
string ToString(System.IFormatProvider provider) поставляет формат по умолчанию, и
string ToString() поставляет формат по умолчанию и поставщика,

+0

Я не вижу вашего мнения о перегрузках. Правда, перегрузки должны делать то же самое, но могут делать это по-разному. Вот почему вы хотите перегрузки. Количество аргументов не имеет значения и может быть одинаковым или различным (пример: различные методы преобразования). О спейсифике компилятора: в зависимости от уровня навыка следующего человека, все может быть «угловым». Пока ваш дизайн имеет смысл и работает интуитивно, это делает его работу. Это может потребовать тщательного рассмотрения времени разработки, а не только тогда, когда класс используется позже. –

+0

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

0

Простое решение заключается в создании другой метод с подписью:

void Method() { } 

или лучше обновить подпись по одному из методов:

void Method(TypeB b = null) { } 

и затем называть это так :

Method(); 

Во время компиляции значение null является нетипизированным и поэтому компилятор не может соответствовать его с одним из сигнатур методов. Во время выполнения все переменные, которые могут быть разрешены на null, будут набираться и поэтому не вызовут проблемы.

3

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

Если у вас есть:

void Method(TypeA a) { } 
void Method(TypeB b) { } 

Вы можете позвонить Method(a: null); или Method(b: null);

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

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