PDA

Vollständige Version anzeigen : Function oder Sub - Optional gesetzt?!


Flämmchen
16.03.2009, 21:20
Hallo zusammen,

ich stehe grade vor einem Problem. Und zwar habe ich mir eine Funktion geschrieben, die etwas erledigen soll. Allerdings hat diese Funktion einen optional-wert. Hier zwei Beispielfunktionen:<FONT COLOR=#0000FF>Private Function</FONT>&nbsp;MyFunc(<FONT COLOR=#0000FF>ByVal</FONT>&nbsp;Var1&nbsp;<FONT COLOR=#0000FF>As String</FONT>,&nbsp;<FONT COLOR=#0000FF>Optional ByVal</FONT>&nbsp;Var2&nbsp;<FONT COLOR=#0000FF>As Integer</FONT>)&nbsp;<FONT COLOR=#0000FF>As Boolean</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#0000FF>If</FONT>&nbsp;Var2&nbsp;=&nbsp;0&nbsp;<FONT COLOR=#0000FF>Then</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#008000>'&nbsp;&nbsp;&nbsp;entweder&nbsp;nicht&nbsp;gesetzt&nbsp;oder&nbsp;0&nbsp;!!</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#0000FF>Else</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#008000>'&nbsp;&nbsp;&nbsp;gesetzt!</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#0000FF>End If</FONT>
<FONT COLOR=#0000FF>End Function</FONT>

<FONT COLOR=#0000FF>Private Function</FONT>&nbsp;MyFunc(<FONT COLOR=#0000FF>ByVal</FONT>&nbsp;Var1&nbsp;<FONT COLOR=#0000FF>As String</FONT>,&nbsp;<FONT COLOR=#0000FF>Optional ByVal</FONT>&nbsp;Var2&nbsp;<FONT COLOR=#0000FF>As String</FONT>)&nbsp;<FONT COLOR=#0000FF>As Boolean</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#0000FF>If</FONT>&nbsp;Var2&nbsp;=&nbsp;""&nbsp;<FONT COLOR=#0000FF>Then</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#008000>'&nbsp;&nbsp;&nbsp;entweder&nbsp;nicht&nbsp;gesetzt&nbsp;oder&nbsp;""(leer)&nbsp;!!</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#0000FF>Else</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#008000>'&nbsp;&nbsp;&nbsp;gesetzt!</FONT>
&nbsp;&nbsp;&nbsp;&nbsp;<FONT COLOR=#0000FF>End If</FONT>
<FONT COLOR=#0000FF>End Function</FONT>
Meine frage ist nun, wie kann ich mit 100%iger genauigkeit sagen, ob die Variable (Var2) angegeben wurde? Sie kann nämlich auch 0 bzw. leer sein. Kann man das überhaupt 100%ig rausfinden - oder geht das gar nicht?

Danke im voraus,

MFG Flämmchen

Nepumuk
16.03.2009, 23:07
Hallo Flämmchen,

das geht nur, wenn du die Parameter als Variant deklarierst. Dann kannst du mit der IsMissing-Funktion abfragen ob etwas übergeben wurde.

<nobr><span style="font-family:Courier New,Arial; font-size:9pt ;" ><span style="color:#000080"; >Option</span> <span style="color:#000080"; >Explicit</span><br /><br /><b><span style="color:#000080"; >Private</span> <span style="color:#000080"; >Function</span> MyFunc(<span style="color:#000080"; >ByVal</span> Var1 <span style="color:#000080"; >As</span> String, <span style="color:#000080"; >Optional</span> <span style="color:#000080"; >ByVal</span> Var2 <span style="color:#000080"; >As</span> <span style="color:#000080"; >Variant</span>) <span style="color:#000080"; >As</span> <span style="color:#000080"; >Boolean</span></b><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#000080"; >If</span> IsMissing(Var2) <span style="color:#000080"; >Then</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MsgBox <span style="color:#800000"; >"nicht gesetzt !!"</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyFunc = <span style="color:#000080"; >False</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#000080"; >Else</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MsgBox Var2<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyFunc = <span style="color:#000080"; >True</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#000080"; >End</span> <span style="color:#000080"; >If</span><br /><b><span style="color:#000080"; >End</span> <span style="color:#000080"; >Function</span></b><br /><br /><b><span style="color:#000080"; >Public</span> <span style="color:#000080"; >Sub</span> Test()</b><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#000080"; >Dim</span> blnReturn <span style="color:#000080"; >As</span> <span style="color:#000080"; >Boolean</span><br />&nbsp;&nbsp;&nbsp;&nbsp;blnReturn = MyFunc(<span style="color:#800000"; >"ABC"</span>)<br />&nbsp;&nbsp;&nbsp;&nbsp;MsgBox blnReturn<br />&nbsp;&nbsp;&nbsp;&nbsp;blnReturn = MyFunc(<span style="color:#800000"; >"YXZ"</span>, 123)<br />&nbsp;&nbsp;&nbsp;&nbsp;MsgBox blnReturn<br /><b><span style="color:#000080"; >End</span> <span style="color:#000080"; >Sub</span></b><br /></span></nobr>

ebs17
16.03.2009, 23:21
das geht nur, wenn du die Parameter als Variant deklarierst
Das würde ich etwas relativieren wollen. Man kann einem optionalen Argument auch einen Standardwert mitgeben, um sich bspw. Argumentangaben (bewusst) zu sparen. Dies wird auch bei VBA-Standardfunktionen vielfach angewandt.
Private Function MyFunc(ByVal Var1 As String, _
Optional ByVal Var2 As Integer = 0, _
Optional ByVal Var3 As String = "", _
Optional ByVal Var4 As Boolean = True) As Boolean

MyFunc = False
If [Auswertung = erfolgreich] Then _
MyFunc = True
End Function


' Aufruf
Call MyFunc("ABC")

' ist gleichlautend zu
Call MyFunc("ABC", 0, "", True)

0 ist Teilmenge von Integer, eine Extraprüfung ist nur für Fälle wie Vermeidung von Division durch 0 u.ä. notwendig.

Argumente, die Null enthalten können, können auch mit Nz entschärft werden:
Call MyFunc("ABC", Nz(FeldX, 0), Nz(FeldY, ""), True)

jinx
17.03.2009, 04:30
Moin, eberhard,

Argumente, die Null enthalten können, können auch mit Nz entschärft werden:
Meines Wissens und durch Nachschlagen verifiziert im Objektkatalog ist Nz in Excel-VBA nicht vorhanden...

Nepumuk
17.03.2009, 08:38
Hallo Eberhard,

das ist schon richtig, aber du weißt damit nicht, ob ein Parameter übergeben wurde. Wenn du einen Standardwert 0 setzt, kann ich ja trotzdem eine 0 übergeben wollen. Ich kann natürlich irgendwelche irrealen Werte als Standard setzen. Aber die muss ich dann auch überall wo ich verzweige abfragen. Das macht das ganze wartungsaufwendiger.

ebs17
17.03.2009, 10:28
Ich kann natürlich irgendwelche irrealen Werte als Standard setzen.
Interessanter Gedanke. Ich würde aber die gezeigte Variante anwenden, um es mir einfacher zu machen. Für eine nicht vorhandene Zahl eine 0 zu verwenden, um rechnen zu können, ist so irreal nicht.

Wenn Du prüfen willst, ob Argumente übergeben werden, ist die Verwendung von Variant natürlich passend. Eigentliches Ziel ist doch aber, dass eine Funktion einen Rückgabewert (Ergebnis einer Berechnung) liefert und dass eine Sub eine Prozedur ausführt, und das fehlerfrei auch bei teilweise unvollständigen Argumenten (die Datenquelle kann man nicht immer beeinflussen).
Hier kann der Vorteil von Variant, alles sein zu können (String, Zahl, Array, Objekt, Null) schnell zur Falle werden. Nicht selten wird nicht nur ein Argument, sondern ein Argument mit richtigem Datentyp benötigt (Excel scheint hier recht tolerant zu sein, eine SQL-Anweisung steigt sofort aus). Diese zusätzliche Prüfung auf Datentyp kann ich mir ersparen, wenn ich per Funktionsdeklaration gleich den richtigen Datentyp einfordere.

Dass man die deklarierte Funktion und den Aufruf dieser in Übereinstimmung bringt mit der vorhandenen Aufgabenstellung und der zugehörigen Datenbasis, versteht sich auch von selbst.

Den Hinweis mit der Verzweigung verstehe ich nicht: Eine Funktion liefert im Normalfall ein Ergebnis (und das hoffentlich ohne festcodierten Objektbezug). Brauche ich mehrere Ergebnisse, rufe ich diese Funktion entsprechend oft auf mit entsprechend variierten Argumenten.

@jinx: Guter Hinweis. Um solche Unterschiede kennenzulernen bin ich auch in diesem Forum unterwegs. Die IIf-Funktion macht aber das Gleiche und könnte als Nz-Ersatzfunktion herangezogen werden.

Nepumuk
17.03.2009, 13:19
Hallo Eberhard,

ich rufe ja nicht nur Funktionen sondern auch ganz normale Routinen mit optionalen Parametern auf. Und diese Routine soll entweder das machen, oder das, oder das ...... Wenn ich damit rechnen muss, dass über die Parameter ganz unterschiedlichen Datentypen in wechselnder Anzahl ankommen können, benutze ich ein Parameterfeld (ParamArray).

So habe ich z.B. eine Standardschleife über alle Zeilen in einem Projekt. Diese Schleife ruft ganz unterschiedliche Routinen auf. Die Namen der aufzurufenden Routinen bekommt die Schleife bei ihrem Aufruf erst mitgeteilt. Dazu bekommt sie auch ganz unterschiedliche Daten geliefert die in den aufgerufenen Routinen benötigt werden. Das können Workbooks sein, Worksheets, irgendwelche Zähler, einen String, welcher die Caption der Progressbar enthält die während der Abarbeitung der Schleife angezeigt wird usw. Da die Anzahl der Parameter variiert, ist das sogenannte ParamArray notwendig.

Ansonsten habe ich meine Programme soweit im Griff, dass ich mir 100% sicher bin, das ich einer Routine, welche optional eine Zeilennummer oder nichts erwartet, kein Objekt übergebe.

ebs17
17.03.2009, 14:02
@Nepumuk:
Ehe wir zu weit von der eingangs gestellten Frage abkommen: Dein Vorschlag in #2 ist in Ordnung. Die absolute Aussage ...
das geht nur, wenn du die Parameter als Variant deklarierst
... ist aber falsch, weil unvollständig. Eine mögliche Alternative habe ich hinreichend dargestellt. Eine "Funktion ... die etwas erledigen soll" umfasst sicher mehr als eine Prüfung auf vorhandene Argumente.

Über unterschiedliche Arbeitsweisen kann man trefflich diskutieren. Das ist aber an der Stelle nicht mein Wille. Meine Aussage, dass man sich mit genau definierten Eingangs- und Ausgangsparametern eine Reihe von sonst notwendigen Prüfungen ersparen kann, ist sicher nachvollziehbar und eben als möglicher Weg zu verstehen.

Nepumuk
17.03.2009, 15:06
Hallo Eberhard,

die Ausgangsfrage war:

Meine frage ist nun, wie kann ich mit 100%iger genauigkeit sagen, ob die Variable (Var2) angegeben wurde?

Und meine Antwort "das geht nur, wenn du die Parameter als Variant deklarierst" ist absolut korrekt.

Deine Alternative lässt es eben nicht zu, das mit 100% Sicherheit zu sagen.

Wenn du einen optionalen boolschen Parameter mit False vorbelegst, wie willst du im Programm unterscheiden, ob es der übergebene Wert oder der Vorgabewert ist?

ebs17
17.03.2009, 17:01
Deine Alternative lässt es eben nicht zu, das mit 100% Sicherheit zu sagen.
Das ist korrekt. Meine Alternative beinhaltet aber einen definierten und einfachen Umgang mit einem nicht angegebenen Argument (= der Sinn von optional). Das ist für mich die eigentliche Problemstellung.

Wenn die Funktion "etwas erledigen soll", ist ein Abbruch nach IsMissing(Var2)=True keine Lösung, wohl aber das Setzen eines verarbeitbaren Ersatzwertes. Das mache ich eben direkt.

Das Parameterfeld ist ein tolles Beispiel: Hier muss nicht nur geprüft werden, ob Argument übergeben, sondern zusätzlich, wie viele Argumente übergeben wurden. Vorbeugend zur Antwort, dass immer die richtigen Parameter in der richtigen Anzahl und Reihenfolge übergeben werden und der Code auch funktioniert (im Griff ist), stelle ich gleich zusätzlich die Frage, ob Du eine Fehlerbehandlung verwendest, und wenn ja, warum?

Meinen Vorschlag (es ist ein Vorschlag, nichts weiter) würde ich so zusammenfassen: Warum soll ich erst die Nadel auf den Heuhaufen werfen, wenn ich sie doch zwischen den Fingerspitzen halten will?

Und: Letzten Endes wird nur Flämmchen beantworten können, was er/sie eigentlich wollte.

Flämmchen
17.03.2009, 20:31
Hallo zusammen,

ich sehe schon, das hat zu einer regen diskussion geführt hier. Ich finde beide ansätze (sowohl Variant/ismissing als auch "alternativwerte") recht interessant und gut zu wissen. Letztendlich kann man dann ja je nach situation die variante anwenden.

Von meiner seite aus ist dieser thread gelöst. Wenn ihr noch weiter diskutieren wollt, tut euch keinen zwang an :P

Vielen Dank,

MFG Flämmchen

ebs17
18.03.2009, 11:56
Hallo Flämmchen, ich für meinen Teil war auch fertig, und Dein Feedback gibt mir die Bestätigung, dass ich mich verständlich ausgedrückt hatte.