C #: передача нулевого значения перегруженному методу - какой метод вызывается? - PullRequest
34 голосов
/ 05 апреля 2009

Скажем, у меня есть две перегруженные версии метода C #:

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

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

Method( null );

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

Ответы [ 6 ]

71 голосов
/ 05 апреля 2009

Это зависит от 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);

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

8 голосов
/ 06 апреля 2009

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

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

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

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

4 голосов
/ 22 апреля 2015

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

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

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

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

1 голос
/ 05 апреля 2009

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

0 голосов
/ 21 марта 2019

Просто введите приведение к тому, что вы хотите, чтобы перегруженное значение было

void method(int);
void method(string);
method((string) null){};

Это будет называть

0 голосов
/ 22 марта 2014

Простым решением является создание другого метода с сигнатурой:

void Method() { }

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

void Method( TypeB b = null ) { }

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

Method();

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

...