Есть ли разница между 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
Вы должны уточнить, почему происходит такое поведение.
Артуро Торрес Санчес
26

Нет, кроме синтаксической разницы нет.

ДжаредПар
источник
3

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

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