tags:

views:

3336

answers:

4

I was looking at some example C# code, and noticed that one example wrapped the return in ()'s.

I've always just done:

return myRV;

Is there a difference doing:

return (myRV);
+27  A: 

No, there is no difference other than syntactical.

JaredPar
Apparently people agree with you :)
chris
@Jared ... there's some easy votes ;-)
spender
@spender, the occasional easier question makes up for all of the hard / in depth answers that receive no votes
JaredPar
It's only because I'm jealous!
spender
Same ordeal with my girlfriend ;)
Hamish Grubijan
Because everyone checks the questions they know the answer to...
BlueRaja - Danny Pflughoeft
@BlueRaja: Until they learn they don't: http://stackoverflow.com/questions/2186595/c-is-there-a-difference-between-return-myvar-vs-return-myvar/2187498#2187498.
Jason
+131  A: 

UPDATE: This question was the subject of my blog on 12 April 2010. Thanks for the amusing question!

In practice, there is no difference.

In theory there could be a difference. There are three interesting points in the C# specification where this could present a difference.

First, conversion of anonymous functions to delegate types and expression trees. Consider the following:

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

F1 is clearly legal. Is F2? Technically, no. The spec says in section 6.5 that there is a conversion from a lambda expression to a compatible delegate type. Is that a lambda expression? No. It's a parenthesized expression that contains a lambda expression.

The Visual C# compiler makes a small spec violation here and discards the parenthesis for you.

Second:

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

F3 is legal. Is F4? No. Section 7.5.3 states that a parenthesized expression may not contain a method group. Again, for your convenience we violate the specification and allow the conversion.

Third:

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

F5 is legal. Is F6? No. The spec states that there is a conversion from the literal zero to any enumerated type. "(0)" is not the literal zero, it is a parenthesis followed by the literal zero, followed by a parenthesis. We violate the specification here and actually allow any compile time constant expression equal to zero, and not just literal zero.

So in every case, we allow you to get away with it, even though technically doing so is illegal.

Eric Lippert
+1 Your detailed knowledge always astounds!
Dan Diplo
+1 I guess we can never take anything for granted in the programming world. I'm sure Mr Lippert could also write a novel if someone asked if there is anything notable to consider when doing `int n = 5;`.
herzmeister der welten
@Eric Lippert: Why the spec violations? I see that you say for convenience but how do you weigh the balance of violating the spec versus being convenient? Was it a bug that became a feature for convenience? :-)
Jason
I think the last word you meant to be *illegal*?
Dan Tao
@Jason: I believe the spec violations in the first two cases are simply errors that were never caught. The initial binding pass historically has been very aggressive about prematurely optimizing expressions, and one of the consequences of that is that parentheses are thrown away very early, earlier than they ought to be. In pretty much every case, all this does is makes programs that are intuitively obvious work the way they ought to, so I'm not very worried about it. Analysis of the third case is here: http://blogs.msdn.com/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx
Eric Lippert
In theory, in practice, there *is* a difference (I'm not sure if Mono allows these 3 cases, and don't know of any other C# compilers, so there may or may not be a difference in practice in practice). Violating the C# spec means your code won't be fully portable. Some C# compilers may, unlike Visual C#, not violate the spec in those particular cases.
Brian
Did you write the blog post yet? I can't find it.
SLaks
@SLaks: I re-ordered the queue. It'll go up April 12th.
Eric Lippert
The blog post is now live: http://blogs.msdn.com/ericlippert/archive/2010/04/12/ignoring-parentheses.aspx
Daniel Daranas
Damn, I'm almost ashamed when I read Lippert insights... I don't think I have knowledge about any subject even close to how much he has about C#.
Bruno Brant
@Bruno: All it takes is about eight or ten thousand hours of study of a given subject and you too can be an expert on it. That's easily doable in four years of full-time work.
Eric Lippert
@Eric, those are ~80 hour weeks! Take a vacation.
Anthony Pegram
@Anthony: 8 hours per day x 5 days per week x 50 weeks per year = 2000 hours per year. 8000 hours / 2000 hours per year = 4 years. Where are you getting 80 hour weeks from in there?
Eric Lippert
@Eric, I'm getting that number from pure failure, that's where. :( I'm usually good at mental math, but not today. I blame getting old.
Anthony Pegram
@Anthony: When I do that I just tell people that my degree is in *mathematics*, not *arithmetic*.
Eric Lippert
@Eric: for a second there, i read that as 8 or 10 thousand *years* of study! :)
RCIX
My heart would totally go for "Don't Violate The Specs Under Any Circumstances". Mainstream languages usually have more than one compiler -we already have mono and MS csc- and this makes specs verification a very necessary facet. This is the only way we can have true portability. The worse part is, once you make such a mistake, you can't revert it because this might break others' code. Bad
Galilyou
In theory, practice and theory are the same but, in pratice they never are.
Sayed Ibrahim Hashimi
+5  A: 

A good way to answer questions like this is to use Reflector and see what IL gets generated. You can learn a lot about compiler optimizations and such by decompiling assemblies.

Bryan
That would certainly answer the question for the one specific case, but that wouldn't necessarily be representative of the entirety of the situation.
Beska
+4  A: 

There are corner cases when presence of parentheses can have effect on the program behavior:

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

Hope you will never see this in practice.

nikov
Not exactly an answer to my question, but still interesting - thanks.
chris
+1 better than eric's answer
Robert Fraser