1.) ByRef bzw. ByVal trifft nur auf natürliche Datentypen z.B. Integer, String, Double etc. Alles, was ein Feld oder ein Objekt ist, wird sowieso nur ByRef übergeben.
2.) Im Normalfall wird man nur ByVal benötigen. Damit wird eine Kopie übergeben. In einem sauber geschriebenen Programm wirst du keine ByRef Argumente finden außer bei externen Funktionen aus einer dll.
3.) Es ist ein Irrglaube, dass ByRef immer schneller ist. Eine Referenz auf einen Integer, Double etc. ist nicht kleiner als der Wert selbst und bei Strings ist das Kopieren der Daten auch relativ schnell (ca. 10GB/s bei langen Ketten, im L1 Cache noch schneller). Wenn man nicht gerade ständig 1K seitige Romane in den Strings speichert, sollte das ziemlich egal sein. Auf der anderen Seite wird bei jedem Zugriff auf die übergebene Variable ein Speicherzugriff mehr benötigt (sowohl lesen, als auch schreiben). Das sind typischerweise 3-4 Takte, was bei häufigem Zugriff doch bremst.
4.) Das Verwenden der Parameter für Rückgabewert ist eine Unart aus der C Zeit, die man auf jeden Fall vermeiden sollte. Der Rückgabewert hat immer mit dem Return Statement zu erfolgen. Sämtliche Fehlercodes werden über Exceptions behandelt. Falls mehr als ein Rückgabewert benötigt wird, dann macht man ein Objekt. Ist das nicht schön möglich, so hat man meistens einen Fehler im Design.
Erstens erkennt man absolut nicht, was was jetzt der Rückgabewert ist und was ein Parameter.
Zweitens kann es sehr leicht passieren, dass man irrtümlich einen Wert überschreibt, den man nicht verändern wollte oder umgekehrt einen Wert nicht hineinschreibt, weil man das byref vergessen hat und auf einmal alte Werte drin stehen. Da sucht man eventuell auch lange.