Подтвердить что ты не робот

Метод перегрузки возвращаемых значений

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

int x = FunctionReturnsIntOrString();

Почему компилятор мешает мне иметь две функции с разными типами возвращаемых данных?

4b9b3361

Ответ 1

Хотя это может быть очевидно в этом конкретном сценарии, существует множество сценариев, где это на самом деле не очевидно. Давайте рассмотрим следующий API для примера

class Bar { 
  public static int Foo();
  public static string Foo();
}

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

void Ambiguous() {
  Bar.Foo();
  object o = Bar.Foo();
  Dog d = (Dog)(Bar.Foo());
  var x = Bar.Foo();
  Console.WriteLine(Bar.Foo());
}

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

Ответ 2

Из последнего параграфа раздела 1.6.6 спецификации языка С# 3.0:

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

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

Ответ 3

У вас есть об использовании дженериков для возврата правильного типа.

public T SomeFunction<T>()
{
    return T
}

int x = SomeFunction<int>();
string y = SomeFunction<string>();

Примечание. Этот код не был протестирован

Ответ 4

Функция, отличающаяся только возвращаемыми значениями, не подходит для перегрузки.

int x = FunctionReturnsIntOrString();
double y = FunctionReturnsIntOrString();

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

FunctionReturnsIntOrString();   //int version
FunctionReturnsIntOrString();   //double version

Компилятор не может разрешить перегруженные методы здесь.

Ответ 5

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

Задайте проблему немного более конкретно. Ключевым моментом здесь, я думаю, является синтаксис. Мы хотим этого:

int intValue = GetData();
string stringValue = GetData();
DateTime[] dateTimeArrayValue = GetData();

Однако мы не хотим, чтобы это работало:

double doubleValue = GetData();
DateTime? nullableDateTimeValue = GetData();

Чтобы это сделать, мы должны использовать промежуточный объект в возвращаемом значении из GetData(), определение которого выглядит так:

public class Data
{
    public int IntData { get; set; }
    public string StringData { get; set; }
    public DateTime[] DataTimeArrayData { get; set; }

    public MultiReturnValueHelper<int, string, DateTime[]> GetData()
    {
        return new MultiReturnValueHelper<int, string, DateTime[]>(
            this.IntData, 
            this.StringData, 
            this.DataTimeArrayData);
    }
}

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

public class MultiReturnValueHelper<T1, T2, T3> : Tuple<T1, T2, T3>
{
    internal MultiReturnValueHelper(T1 item1, T2 item2, T3 item3)
        : base(item1, item2, item3)
    {
    }

    public static implicit operator T1(MultiReturnValueHelper<T1, T2, T3> source)
    {
        return source.Item1;
    }

    public static implicit operator T2(MultiReturnValueHelper<T1, T2, T3> source)
    {
        return source.Item2;
    }

    public static implicit operator T3(MultiReturnValueHelper<T1, T2, T3> source)
    {
        return source.Item3;
    }
}

Повторите это определение для T1, T2, T3 и т.д. для общего случая.

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

Другим приложением является включение синтаксиса следующего вида:

data["Hello"] = "World";
data["infamy"] = new DateTime(1941, 12, 7);
data[42] = "Life, the universe, and everything";

Точный механизм для выполнения этого синтаксиса оставлен как упражнение для читателя.

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

Ответ 6

Я думаю, вы хотите серьезно пересмотреть то, что вы делаете и как, но вы МОЖЕТЕ сделать это:

int i = FunctionReturnsIntOrString<int>();
string j = FunctionReturnsIntOrString<string>();

реализуя его следующим образом:

private T FunctionReturnsIntOrString<T>()
{
    int val = 1;
    if (typeof(T) == typeof(string) || typeof(T) == typeof(int))
    {
        return (T)(object)val;
    }
    else
    {
        throw new ArgumentException("or some other exception type");
    }
}

но есть много причин, чтобы не делать этого.

Ответ 7

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

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

Ответ 8

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

void SomeMethod(int x) { ... }
void SomeMethod(string x) { ... }

В этом контексте, если вы вызываете SomeMethod(FunctionReturnsIntOrString()), что должен делать компилятор?

Ответ 9

В С# нет типов возвратов overridin, IL поддерживает этот тип переопределения, но С# еще не существует.

Ответ 10

Вам также не требуется назначать значение вызываемому методу. Например:

int FunctionReturnsIntOrString() {return 0;}
string FunctionReturnsIntOrString() {return "some string";}

//some code
//...

int integer = FunctionReturnsIntOrString(); //It probably could have figured this one out

FunctionReturnsIntOrString(); //this is valid, which method would it call?