2016-08-25 14 views
2

私はいくつかのコードのために開発した構造体にアクセスするためのWith文を持っています。構造体は(私が働いているところに)MaleとFemaleの2つの枝があり、私はサブルーチンにIntegerを渡してセックスを指定しています。構造体を持つVBA内のIIF

大雑把その後、私のサブルーチンで私はWITHにIIF文を置くことによって、内部IF文を取り除くことができれば私の質問がある

With PopData.Ages(Cur_Age) 
    If Cur_Sex = 0 Then 
     ... Do the stuff for males 
    Else 
     ... Do the exact same stuff but for females 
    End If 
End With 

を持って、私は

Type MyDetails 
    green As Integer 
    black As Integer 
    yellow As Integer 
    blue As Integer 
End Type 

Type Genders 
    Males As MyDetails 
    Females As MyDetails 
End Type 

Type GlobalData 
    RegionName As String 
    Ages(0..100) As Genders 
End Type 

Public Dim PopData As GlobalData 

を持って話しますブロック...または同等の何か。コードは正常に機能するので、これは主に学問的な質問ですが、。メイルズをすべて変更するという単純な違いですべてのコードを複製する必要はありません。~。女性。 If Cur_Sex = 0 Thenブロックの各部分内の

ありがとうございます。これが理にかなってほしい。私はうまくいけば、それをより明確にするために、問題の実際のコードを追加している

...

Cur_Ethは DistInfoは人口の人種分布 Cur_Eth_Totalは、の和である二重であるため、短いです現在の人種の略です。 D + .M + .I + .N(私がルーチンを呼び出すときに男性または女性のどちらかに応じて)。

With PopData(Cur_CSD).DistInfo 
    Cur_Rand = Rnd 
    Cur_Eth = -1 
    If Cur_Sex = 0 Then 
     Cur_Eth_Total = .Males.D + .Males.M + .Males.I + .Males.N 
     Select Case Cur_Rand 
      Case Is < CDbl(.Males.D)/Cur_Eth_Total 
       Cur_Eth = 0 
      Case Is < CDbl(.Males.D + .Males.M)/Cur_Eth_Total 
       Cur_Eth = 1 
      Case Is < CDbl(.Males.D + .Males.M + .Males.I)/Cur_Eth_Total 
       Cur_Eth = 2 
      Case Else 
       Cur_Eth = 3 
     End Select 
    Else 
     Cur_Eth_Total = .Females.D + .Females.M + .Females.I + .Females.N 
     Select Case Cur_Rand 
      Case Is < CDbl(.Females.D)/Cur_Eth_Total 
       Cur_Eth = 0 
      Case Is < CDbl(.Females.D + .Females.M)/Cur_Eth_Total 
       Cur_Eth = 1 
      Case Is < CDbl(.Females.D + .Females.M + .Females.I)/Cur_Eth_Total 
       Cur_Eth = 2 
      Case Else 
       Cur_Eth = 3 
     End Select 
    End If 
End With 
+2

あなたは、タイプではなくクラスが必要なように聞こえます。 – Comintern

+0

興味深い考え。あなたは正しいかもしれません、元々クラスでそれを構築することが賢明かもしれません。現時点では、この問題を解決するためにすべてのコードを書き直すつもりはないと思います。それは働いている...私はそれを維持することをより簡単にしたいと考えていた。しかし、将来のための良い考え。 –

+0

一般に、クラスにカプセル化できる機能が多くなればなるほど、維持しやすくなります。 [SOLIDデザイン](https://en.wikipedia.org/wiki/SOLID_(object-oriented_design))を参照してください。 – Comintern

答えて

1

がこれは@cyboashuでの提案に基づいています(元の回答は私が使っているものではないにもかかわらず、私は彼の答えを受け入れられた解決策としてマークします)。代わりに私が使用した型宣言文の

、代わりに私が使用します:私は

Type MyDetails 
    green(1) As Integer ' For documentation purposes I could, perhaps should have used (0 To 1) to be explicit about the array dimensions. 
    black(1) As Integer 
    yellow(1) As Integer 
    blue(1) As Integer 
End Type 

Type GlobalData 
    RegionName As String 
    Ages(0..100) As MyDetails 
End Type 

その後、私は声明を有する得られたが、それがあるとまったく同じだったかもしれないが、コードは(セックスを組み込むためにわずかに変化するであろう「半分のコードの量を切るが、実質的に少ない読み取り可能なコードをしていない)

With PopData(Cur_CSD).DistInfo 
    Cur_Rand = Rnd 
    Cur_Eth = -1 
    Cur_Eth_Total = .D(Cur_Sex) + .M(Cur_Sex) + .I(Cur_Sex) + .N(Cur_Sex) 
    Select Case Cur_Rand 
     Case Is < CDbl(.D(Cur_Sex))/Cur_Eth_Total 
      Cur_Eth = 0 
     Case Is < CDbl(.D(Cur_Sex) + .M(Cur_Sex))/Cur_Eth_Total 
      Cur_Eth = 1 
     Case Is < CDbl(.D(Cur_Sex) + .M(Cur_Sex) + .I(Cur_Sex))/Cur_Eth_Total 
      Cur_Eth = 2 
     Case Else 
      Cur_Eth = 3 
    End Select 
End With 

この概念の実装にここに私の実際のコードを書き換えるでしょう。

+0

でも、私は本当に@GSergから答えを得ました。 –

+2

'Public Enum Sexes:Male:Female:End Enum'型MyDetails:green(Male To Female)As Integer:black(Male To Female)As Integer:yellow(Male To女性)As Integer:青(男性から女性)As Integer:End Type' – GSerg

+0

良い点@GSerg ...私は深刻なコーディングをして以来、ずっとずっと続いています(私はプロジェクト管理をしています)...知っていた列挙された型は便利です...ちょうどそれらの構文を見直す時間がかかりませんでした。素早くリフレッシュしてくれてありがとう! –

6

IIfは、それを介してメソッドを呼び出すために非常に非効率的です。あなたが単純な変数の代入をしている限り、問題はありませんが、テスト条件を使って関数を呼び出そうとすると、問題が発生します。 IIfのものは、条件のTrueまたはFalseステータスにかかわらず、実際に両方のパラメータを評価/実行します。

これは、コードの点では、実行の面で非常に非効率的であるかもしれません。

例を参照してください:私が理解したよう原則的にこれをしたい、

Function test1() As String 
    MsgBox "Test1 called" 
    test1 = "test1" 
End Function 

Function test2() As String 
    MsgBox "Test2 called" 
    test2 = "test2" 
End Function 

Sub Test() 

    Dim x As String 
    Dim Y As Long 
    Dim Z As String 

    x = 0 

    Y = IIf(x = 1, 1, 2) 
    MsgBox Y 

    '/ Although condition evaluates to false, it will still call test1 
    '/which is bound to true condition,before calling test2. 
    Z = IIf(x = 1, test1, test2) 
    MsgBox Z 

End Sub 
+0

IIFが非効率であるかもしれないことに感謝します。しかし、私は関数を呼び出そうとしていません...私が作成したType構造内の値を数学的に計算しています。将来のメンテナーのためにそれをあいまいにすることなく、簡単に行うことができます。だから、これは本当に私の質問に全く対処していないようです。 –

+0

あなたはもっと管理しやすくしようとしているコードを投稿できますか? – cyboashu

+2

@NWT_Bob 10歳以上のVBAコードを維持しています。そして、私は、あなたの「IIF」が**極端に単純でない限り(上記の例のように)、それは時間の経過とともに維持する悪夢になると言います。そして、これらの余分な4行は、それをより保守性にするものではありません。 – litelite

3

を:

With PopData.Ages(Cur_Age) 
    Dim ToProcess as Genders 

    If Cur_Sex = 0 Then 
     ToProcess = .Males 
    Else 
     ToProcess = .Females 
    End If 

    ' Use ToProcess 
End With 

あなたがクラスや構造の両方でそれを行うことができますが、異なる効率および追加のステップとデータをPopData.Ages(Cur_Age)に書き戻したい場合に備えて作成する必要があります。

Gendersがクラスだった場合、あなたが書くことができると思います:

With PopData.Ages(Cur_Age) 
    Dim ToProcess as Genders 

    If Cur_Sex = 0 Then 
     Set ToProcess = .Male 
    Else 
     Set ToProcess = .Female 
    End If 

    ' Use ToProcess here. Both read and write actions will affect the original `PopData.Ages(Cur_Age)`. 
End With 

それは構造なので、あなたの代わりに必要です

With PopData.Ages(Cur_Age) 
    Dim ToProcess as Genders 

    If Cur_Sex = 0 Then 
     ToProcess = .Male 
    Else 
     ToProcess = .Female 
    End If 

    ' Use ToProcess here. 
    ' If you only want to read from ToProcess, that's all you need. 
    ' If you need to write changes back to PopData.Ages(Cur_Age), then you will also need: 

    If Cur_Sex = 0 Then 
     .Male = ToProcess 
    Else 
     .Female = ToProcess 
    End If 
End With 
関連する問題