DOBON.NETプログラミング道掲示板

■34412 / 2階層)  共変性と反変性ってこういうこと?
□投稿者/ 魔界の仮面弁士 大御所(1269回)-(2019/12/06(Fri) 11:46:15)
  • アイコン2019/12/06(Fri) 20:08:09 編集(投稿者)

    No34411に補足(魔界の仮面弁士の記事)
    > https://www.atmarkit.co.jp/fdotnet/chushin/vb2010features_01/vb2010features_01_03.html

    Visual Basic におけるジェネリックの共変性と反変性については
    上記記事でも記載されていますが、追加で説明してみます。


    たとえば、
     Dim txt As TextBox = Me.TextBox1
     Dim item As Control = txt
    というコードが問題無いことは分かりますよね。
    TextBox は Cotrol を継承していますので、問題なく代入できるわけです。


    では何故、下記のコードはエラーになるのでしょう?

    Public Class Form1
      Private items As List(Of Control)

      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim textBoxes As New List(Of TextBox)
        textBoxes.Add(Me.TextBox1)
        textBoxes.Add(Me.TextBox2)
        textBoxes.Add(Me.TextBox3)

        Me.items = textBoxes 'コンパイルエラー
      End Sub
    End Class


    TextBox 型は Control を継承しているので、一見問題無さそうに見えます。

    しかし、上記のような代入を認めてしまうと、
      Me.items(0) = Me.Button1
    のように、TextBox 以外のインスタンスに書き換えられてしまう恐れが生じます。

    Me.items が参照しているのが List(Of TextBox) のインスタンスだとしたら、
    そこに Button という、TextBox 以外のデータが格納されるのはマズイわけです。

    そのため、List(Of TextBox) のインスタンスを List(Of Control) 型の変数に
    代入することはできないように制限されています。
    ※その逆に、List(Of Control) 型のインスタンスを List(Of TextBox) 型に渡すのも駄目です。



    さて上記では、代入によって書き換えられるのが問題であるとされています。

    では、型パラメーターが「常に取得専用」でのみ使われる場合はどうでしょうか?
    items 変数の型を下記のように変更した場合は、エラーにはならずに済みます。

    Public Class Form1
      'Private items As List(Of Control)
      Private items As IEnumerable(Of Control)

      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim textBoxes As New List(Of TextBox)
        textBoxes.Add(Me.TextBox1)
        textBoxes.Add(Me.TextBox2)
        textBoxes.Add(Me.TextBox3)

        Me.items = textBoxes 'IEnumerable(Of ) への代入は OK
      End Sub
    End Class


    上記の処理が認められているのは、List(Of T) の時とは違って「入力」ができないためです。
    たとえば、List(Of Control) だと行えていた下記の処理は、IEnumerable(Of Control) だと行えません。
      Me.items(0) = Me.Button1


    IEnumerable(Of T) インターフェイスでは、型パラメーターで指定された
    T 型もしくは T を継承した型を、「出力方向だけ」にしか使用しない仕様です。

    そこでこのように、
     『指定された型パラメーターが、出力(戻り値、ReadOnly Property)にしか使われない』
    ことを明確に伝えられるよう、VB2010 からは Out キーワードをつけて宣言できるようになりました。

    > Public Interface IEnumerable(Of Out T)


    上記を踏まえて、先ほどの共変性と反変性の件と In / Out キーワードがどう関わってくるかを見てみます。


    '=== 「In」なので「入力」にしか使われていない
    Public Interface ISample1(Of In T As Control)
      'この型パラメータ T では In によって『共変性』が保証されており、
      ' T 型は常に「広い型から狭い型へ変換するだけ」の実装になっている
      Sub Test(ByVal x As T)
      WriteOnly Property Prop As T
      'たとえば Of Control で実装された場合に、上記メンバーに対して
      'Label インスタンスや Form インスタンスを「入力」したとしても、何の問題も無い
    End Interface


    '=== 「Out」なので「出力」にしか使われていない
    Public Interface ISample2(Of Out T As Control)
      'この型パラメータ T では Out によって『反変性』が保証されており、
      ' T 型は常に「狭い型から広い型へ変換するだけ」の実装になっている
      Function Test() As T
      ReadOnly Property Prop As T
      'たとえば Of Control の場合に、上記メンバーの戻り値から
      'Label インスタンスや Form インスタンスが「出力」されてきたとしても、何の問題も無い
    End Interface


    '=== 双変の場合(つまり共変と反変の両方がありうる場合)は
    '=== In も Out も付与できない(付与するとコンパイルエラー)
    Public Interface ISample3(Of T As Control)
      Sub Test(ByRef x As T)
    End Interface
    Public Interface ISample4(Of T As Control)
      Property Prop As T
    End Interface
違反を報告
削除キー/

前の記事(元になった記事) 次の記事(この記事の返信)
←Re[1]: 共変性と反変性ってこういうこと? /魔界の仮面弁士 →Re[3]: 共変性と反変性ってこういうこと? /VBはじめました
 
上記関連ツリー

Nomalアイコン 共変性と反変性ってこういうこと? / VBはじめました (19/12/05(Thu) 13:36) #34410
Nomalアイコン Re[1]: 共変性と反変性ってこういうこと? / 魔界の仮面弁士 (19/12/06(Fri) 11:05) #34411
  └Nomalアイコン 共変性と反変性ってこういうこと? / 魔界の仮面弁士 (19/12/06(Fri) 11:46) #34412 ←Now
    └Nomalアイコン Re[3]: 共変性と反変性ってこういうこと? / VBはじめました (19/12/06(Fri) 23:46) #34413
      └Nomalアイコン Re[4]: 共変性と反変性ってこういうこと? / VBはじめました (19/12/06(Fri) 23:47) #34414 解決み!

All 上記ツリーを一括表示 / 上記ツリーをトピック表示
 
上記の記事へ返信

Mode/  Pass/


- Child Tree -