Có sự khác biệt giữa return myVar và return (myVar) không?


87

Tôi đang xem một số mã C # ví dụ và nhận thấy rằng một ví dụ đã bao gồm return trong ().

Tôi luôn luôn làm:

return myRV;

Có sự khác biệt khi làm:

return (myRV);

Câu trả lời:


229

CẬP NHẬT: Câu hỏi này là chủ đề trên blog của tôi vào ngày 12 tháng 4 năm 2010 . Cảm ơn vì câu hỏi thú vị!

Trong thực tế, không có sự khác biệt.

Về lý thuyết có thể có một sự khác biệt. Có ba điểm thú vị trong đặc tả C # nơi điều này có thể tạo ra sự khác biệt.

Đầu tiên, chuyển đổi các hàm ẩn danh thành các kiểu ủy quyền và cây biểu thức. Hãy xem xét những điều sau:

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

F1rõ ràng là hợp pháp. Là F2? Về mặt kỹ thuật, không. Thông số kỹ thuật cho biết trong phần 6.5 rằng có sự chuyển đổi từ biểu thức lambda sang kiểu đại biểu tương thích. Đó có phải là một biểu thức lambda không? Không. Đó là một biểu thức được đặt trong ngoặc đơn chứa biểu thức lambda .

Trình biên dịch Visual C # thực hiện một vi phạm thông số kỹ thuật nhỏ ở đây và loại bỏ dấu ngoặc đơn cho bạn.

Thứ hai:

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

F3là hợp pháp. Là F4? Không. Phần 7.5.3 nói rằng một biểu thức trong ngoặc đơn có thể không chứa một nhóm phương thức. Một lần nữa, để thuận tiện cho bạn, chúng tôi vi phạm đặc điểm kỹ thuật và cho phép chuyển đổi.

Ngày thứ ba:

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

F5là hợp pháp. Là F6? Không. Thông số kỹ thuật nói rằng có sự chuyển đổi từ số 0 theo nghĩa đen sang bất kỳ kiểu liệt kê nào. " (0)" không phải là số 0 theo nghĩa đen, nó là dấu ngoặc đơn theo sau là số 0 theo nghĩa đen, theo sau là dấu ngoặc đơn. Chúng tôi vi phạm đặc điểm kỹ thuật ở đây và thực sự cho phép bất kỳ biểu thức hằng số thời gian biên dịch nào bằng 0 chứ không chỉ bằng 0 theo nghĩa đen.

Vì vậy, trong mọi trường hợp, chúng tôi cho phép bạn thoát khỏi nó, mặc dù về mặt kỹ thuật, làm như vậy là bất hợp pháp.


12
@Jason: Tôi tin rằng các vi phạm đặc điểm kỹ thuật trong hai trường hợp đầu tiên chỉ đơn giản là những lỗi chưa bao giờ bị bắt. Đường ràng buộc ban đầu trong lịch sử đã rất tích cực về việc tối ưu hóa quá sớm các biểu thức, và một trong những hậu quả của việc đó là dấu ngoặc đơn bị loại bỏ rất sớm, sớm hơn so với những gì chúng phải làm. Trong hầu hết mọi trường hợp, tất cả điều này là làm cho các chương trình rõ ràng trực quan hoạt động theo cách mà chúng phải làm, vì vậy tôi không lo lắng lắm về điều đó. Phân tích trường hợp thứ ba ở đây: blog.msdn.com/ericlippert/archive/2006/03/28/…
Eric Lippert

6
Về lý thuyết, trong thực tế, có một sự khác biệt (Tôi không chắc chắn nếu Mono cho phép những 3 trường hợp, và không biết về bất kỳ C # trình biên dịch khác, do đó có thể hoặc không thể là một sự khác biệt trong thực tế trong thực tế). Vi phạm thông số C # có nghĩa là mã của bạn sẽ không hoàn toàn di động. Không giống như Visual C #, một số trình biên dịch C # có thể không vi phạm thông số kỹ thuật trong những trường hợp cụ thể đó.
Brian

18
@Bruno: Tất cả chỉ cần khoảng tám hoặc mười nghìn giờ học về một chủ đề nhất định và bạn cũng có thể là một chuyên gia về nó. Điều đó có thể dễ dàng làm được trong bốn năm làm việc toàn thời gian.
Eric Lippert

32
@Anthony: Khi tôi làm điều đó, tôi chỉ nói với mọi người rằng bằng cấp của tôi là toán học , không phải số học .
Eric Lippert

7
Về lý thuyết, thực hành và lý thuyết đều giống nhau, nhưng trên thực tế thì chúng không bao giờ giống nhau.
Nói lời Ibrahim Hashimi

40

Có những trường hợp góc khi sự hiện diện của dấu ngoặc đơn có thể ảnh hưởng đến hoạt động của chương trình:

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

Hy vọng bạn sẽ không bao giờ thấy điều này trong thực tế.


Không chính xác là một câu trả lời cho câu hỏi của tôi, nhưng vẫn thú vị - cảm ơn.
chris

1
Bạn có thể giải thích những gì đang xảy ra trong 2 ở đây?
Eric

2
Bạn nên giải thích tại sao hành vi này xảy ra.
Arturo Torres Sánchez

26

Không, không có sự khác biệt nào ngoài cú pháp.


3

Một cách tốt để trả lời những câu hỏi như thế này là sử dụng Reflector và xem IL được tạo ra. Bạn có thể học rất nhiều về tối ưu hóa trình biên dịch và những thứ như vậy bằng cách dịch ngược các hợp ngữ.


6
Điều đó chắc chắn sẽ trả lời câu hỏi cho một trường hợp cụ thể, nhưng điều đó không nhất thiết phải đại diện cho toàn bộ tình huống.
Beska

Không đồng ý. Nó cung cấp cho người đó một hướng để trả lời câu hỏi.
Bryan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.