Чи існує різниця між return myVar та return (myVar)?


87

Я дивився на приклад коду C # і помітив, що один приклад обернув повернення в ().

Я завжди тільки що робив:

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
@ Джейсон: Я вважаю, що порушення специфікацій у перших двох випадках - це просто помилки, які ніколи не були виявлені. Історичний початковий перехід для прив’язки був дуже агресивним щодо передчасної оптимізації виразів, і одним із наслідків цього є те, що дужки викидаються дуже рано, раніше, ніж мали б бути. Практично в кожному випадку все це робить програми, які інтуїтивно очевидні, працюють так, як їм слід, тому я не дуже переживаю з цього приводу. Аналіз третьої справи - тут: blogs.msdn.com/ericlippert/archive/2006/03/28/…
Ерік Ліпперт

6
Теоретично на практиці є різниця (я не впевнений, що Mono допускає ці 3 випадки, і не знаю жодного іншого компілятора C #, тому на практиці може бути різниця або не бути). Порушення специфікації C # означає, що ваш код не буде повністю портативним. Деякі компілятори C # можуть, на відміну від Visual C #, не порушувати специфікацію в цих конкретних випадках.
Брайан

18
@Bruno: Все, що потрібно, - це близько восьми чи десяти тисяч годин вивчення даної теми, і ти теж можеш бути експертом з цього питання. Це легко здійснити за чотири роки повного робочого дня.
Eric Lippert,

32
@Anthony: Коли я це роблю, я просто кажу людям, що мій ступінь з математики , а не з арифметики .
Ерік Ліпперт,

7
Теоретично практика і теорія є однаковими, але на практиці вони ніколи не бувають.
Sayed Ibrahim Hashimi

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;
}

Сподіваюся, ви ніколи цього не побачите на практиці.


Не зовсім відповідь на моє запитання, але все ж цікавий - дякую.
chris

1
Чи можете ви пояснити, що відбувається в 2 тут?
Ерік

2
Вам слід детальніше пояснити, чому відбувається така поведінка.
Артуро Торрес Санчес,


3

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


6
Це, безперечно, відповіло б на запитання для конкретного випадку, але це не обов'язково було б репрезентативним для всієї ситуації.
Беска

Не згодні. Це дає людині вказівку відповісти на питання.
Брайан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.