Есть ли разница между return myVar и return (myVar)?


87

Я смотрел пример кода C # и заметил, что один пример заключил return в ().

Я всегда только что делал:

return myRV;

Есть ли разница в выполнении:

return (myRV);

Ответы:


229

ОБНОВЛЕНИЕ: этот вопрос был темой моего блога 12 апреля 2010 года . Спасибо за забавный вопрос!

На практике разницы нет.

В теории не может быть разница. В спецификации C # есть три интересных момента, в которых это может иметь значение.

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

Func<int> F1() { return ()=>1; }
Func<int> F2() { return (()=>1); }

F1явно законно. Есть F2? Технически нет. В спецификации говорится в разделе 6.5, что существует преобразование лямбда-выражения в совместимый тип делегата. Это лямбда-выражение ? Нет. Это выражение в скобках , содержащее лямбда-выражение .

Компилятор Visual C # делает здесь небольшое нарушение спецификации и отбрасывает скобки за вас.

Второй:

int M() { return 1; }
Func<int> F3() { return M; }
Func<int> F4() { return (M); }

F3законно. Есть F4? Нет. В разделе 7.5.3 сказано, что выражение в скобках не может содержать группу методов. Опять же, для вашего удобства мы нарушаем спецификацию и разрешаем преобразование.

Третий:

enum E { None }
E F5() { return 0; }
E F6() { return (0); }

F5законно. Есть F6? Нет. В спецификации указано, что существует преобразование буквального нуля в любой перечислимый тип. " (0)" не является буквальным нулем, это скобка, за которой следует буквальный ноль, за которой следует скобка. Мы нарушаем здесь спецификацию и фактически позволяем любому выражению константы времени компиляции равняться нулю , а не только буквальному нулю.

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


12
@Jason: Я считаю, что нарушения спецификации в первых двух случаях - это просто ошибки, которые никогда не были обнаружены. Первоначальный проход привязки исторически очень агрессивно относился к преждевременной оптимизации выражений, и одним из последствий этого является то, что скобки отбрасываются очень рано, раньше, чем они должны быть. Практически в каждом случае все это приводит к тому, что интуитивно очевидные программы работают так, как должны, поэтому меня это не очень беспокоит. Анализ третьего случая находится здесь: blogs.msdn.com/ericlippert/archive/2006/03/28/…
Эрик Липперт

6
В теории, на практике, есть это разница (я не уверен , если Mono позволяет эти 3 случая, и не знают о каких - либо других компиляторов C #, так что может или не может быть разница в практике на практике). Нарушение спецификации C # означает, что ваш код не будет полностью переносимым. Некоторые компиляторы C # могут, в отличие от Visual C #, не нарушать спецификацию в этих конкретных случаях.
Брайан

18
@Bruno: Все, что нужно, - это восемь или десять тысяч часов изучения данного предмета, и вы тоже можете быть в нем экспертом. Это легко сделать за четыре года работы на полную ставку.
Эрик Липперт,

32
@Anthony: Когда я это делаю, я просто говорю людям, что у меня степень по математике , а не по арифметике .
Эрик Липперт,

7
Теоретически практика и теория - одно и то же, но на практике - никогда.
Саид Ибрагим Хашими

40

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

1.

using System;

class A
{
    static void Foo(string x, Action<Action> y) { Console.WriteLine(1); }
    static void Foo(object x, Func<Func<int>, int> y) { Console.WriteLine(2); }

    static void Main()
    {
        Foo(null, x => x()); // Prints 1
        Foo(null, x => (x())); // Prints 2
    }
}

2.

using System;

class A
{
    public A Select(Func<A, A> f)
    {
        Console.WriteLine(1);
        return new A();
    }

    public A Where(Func<A, bool> f)
    {
        return new A();
    }

    static void Main()
    {
        object x;
        x = from y in new A() where true select (y); // Prints 1
        x = from y in new A() where true select y; // Prints nothing
    }
}

3.

using System;

class Program
{
    static void Main()
    {
        Bar(x => (x).Foo(), ""); // Prints 1
        Bar(x => ((x).Foo)(), ""); // Prints 2
    }

    static void Bar(Action<C<int>> x, string y) { Console.WriteLine(1); }
    static void Bar(Action<C<Action>> x, object y) { Console.WriteLine(2); }
}

static class B
{
    public static void Foo(this object x) { }
}

class C<T>
{
    public T Foo;
}

Надеюсь, вы никогда не увидите этого на практике.


Не совсем ответ на мой вопрос, но все же интересно - спасибо.
Крис

1
Вы можете объяснить, что здесь происходит в 2?
Эрик

2
Вы должны уточнить, почему происходит такое поведение.
Артуро Торрес Санчес


3

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


6
Это определенно ответит на вопрос для одного конкретного случая, но не обязательно будет репрезентативным для всей ситуации.
Beska

Не согласен. Это дает человеку направление ответить на вопрос.
Брайан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.