Patrzyłem na przykładowy kod C # i zauważyłem, że jeden przykład zawinął zwrot w ().
Zawsze robiłem:
return myRV;
Czy jest różnica w wykonywaniu:
return (myRV);
Odpowiedzi:
AKTUALIZACJA: To pytanie było tematem mojego bloga 12 kwietnia 2010 roku . Dzięki za zabawne pytanie!
W praktyce nie ma różnicy.
W teorii może istnieć różnica. W specyfikacji C # są trzy interesujące punkty, w których może to stanowić różnicę.
Po pierwsze, konwersja funkcji anonimowych na typy delegowane i drzewa wyrażeń. Rozważ następujące:
Func<int> F1() { return ()=>1; }
Func<int> F2() { return (()=>1); }
F1
jest wyraźnie legalne. Jest F2
? Technicznie nie. Specyfikacja mówi w sekcji 6.5, że następuje konwersja wyrażenia lambda na zgodny typ delegata. Czy to wyrażenie lambda ? Nie. To wyrażenie w nawiasach zawierające wyrażenie lambda .
Kompilator Visual C # dokonuje tutaj niewielkiego naruszenia specyfikacji i odrzuca za Ciebie nawias.
Druga:
int M() { return 1; }
Func<int> F3() { return M; }
Func<int> F4() { return (M); }
F3
jest legalne. Jest F4
? Nie. Sekcja 7.5.3 stwierdza, że wyrażenie w nawiasach nie może zawierać grupy metod. Ponownie, dla Twojej wygody naruszamy specyfikację i zezwalamy na konwersję.
Trzeci:
enum E { None }
E F5() { return 0; }
E F6() { return (0); }
F5
jest legalne. Jest F6
? Nie. Specyfikacja stwierdza, że następuje konwersja z literalnego zera do dowolnego typu wyliczeniowego. „ (0)
” nie jest dosłownym zerem, jest to nawias, po którym następuje dosłowne zero, po którym następuje nawias. Naruszamy tutaj specyfikację i faktycznie zezwalamy na dowolne wyrażenie stałej czasu kompilacji równe zero , a nie tylko dosłowne zero.
Dlatego w każdym przypadku pozwalamy ci ujść na sucho, nawet jeśli technicznie jest to nielegalne.
Istnieją przypadki narożne, w których obecność nawiasów może mieć wpływ na zachowanie programu:
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;
}
Mam nadzieję, że nigdy nie zobaczysz tego w praktyce.
Dobrym sposobem na udzielenie odpowiedzi na takie pytania jest użycie Reflectora i sprawdzenie, jakie IL jest generowane. Możesz dowiedzieć się wiele o optymalizacji kompilatora i tym podobnych, dekompilując zestawy.