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

■34410 / 親記事)  共変性と反変性ってこういうこと?
  
□投稿者/ VBはじめました 一般人(25回)-(2019/12/05(Thu) 13:36:59)
  • アイコン環境/言語:[VisualStudio2017 VB.net] 
    分類:[.NET] 

    ジェネリックを勉強していると、共変性と反変性ってでてきます。
    ググればググるほどよくわからなくなるのですが、次ような解釈で問題ないでしょうか?
    詳しい方いらっしゃいましたら教えて頂けないでしょうか?
    
    (以下、自分なりの解釈)
    ジェネリック型は、さまざまなデータ型に対して、同じ機能を実行させるために必要な処理を行うプログラミング機能を提供します。ジェネリック型のメソッドは、呼び出し側が提供するデータ型に合わせてデータ型を指定してインスタンスを作成して使用することになります。その際、完全にデータ型を合致させることが望ましいのですが、場合によってが、融通性を持たせたい場合が発生します。例えば、List<String>型データとArray型のデータに対して同じ機能を実行いたい場合、List<String>型とArray型に対するジェネリック型インスタンスをそれぞれ作って使用すればいいことになります。しかし、ジェネリック型メソッドがデータソースに求める機能が列挙をベースとしたものであれば、IEnumerable<String>型で定義すれば、一つのデリゲート定義でList<String>型とArray型のデータソースが受け取れるという融通性を得ることができます。この際、呼び出し側のデータ型と受け取り側のデータ型が異なるため、データ変換処理が発生します。List<T>クラス、Arrayクラスは、共にIEnumerable<T>を実装していますので、IEnumerable<T>と比べて強い派生型(狭い型)と言えます。即ち、強い派生型から弱い派生型への型変換がなされたということになります。その際に、データとしての互換性がある状況の場合を反変性があると言います。同様な状況は、メソッドからの返り値の受け渡しでも発生し、返り値の場合は、逆に弱い派生型から強い派生型へ変換する必要があり共変性といいます。次の例は、List(Of Integer)型,Array型をIEnumerable(Of Integer)型として受け取り、なんらかの処理をした後、IEnumerable(Of  Integer)型を返し、List(Of  Integer)型、Array型として受け取った。というものです。
    
    (サンプルコード)
       Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim myList As New List(Of Integer)(New Integer() {1, 2, 3, 4, 5})
            Console.WriteLine("渡したデータ型は、{0}", myList.GetType)
            Dim RetList = Enume(myList)
            Console.WriteLine("返されたデータ型は、{0}", RetList.GetType)
            Console.WriteLine("---------")
            Dim myArray() As Integer = {1, 2, 3, 4, 5}
            Console.WriteLine("渡したデータ型は、{0}", myArray.GetType)
            Dim te2 = Enume(myArray)
            Console.WriteLine("返されたデータ型は、{0}", te2.GetType)
        End Sub
        Function Enume(MyEnume As IEnumerable(Of Integer)) 
                           As IEnumerable(Of Integer)
            Console.WriteLine("受け取ったデータ型は、{0}", MyEnume.GetType)
            '何らかの処理をしたと仮定
            Return MyEnume
        End Function
    
    解釈の内容が正しいのかと、サンプルとして適切かどうか?
    教えて頂けないでしょうか?

マルチポストを報告
違反を報告
引用返信 削除キー/
■34411 / ResNo.1)  Re[1]: 共変性と反変性ってこういうこと?
□投稿者/ 魔界の仮面弁士 大御所(1268回)-(2019/12/06(Fri) 11:05:31)
  • アイコン2019/12/06(Fri) 20:03:48 編集(投稿者)

    No34410に返信(VBはじめましたさんの記事)
    > (以下、自分なりの解釈)
    改行無い 苦行じゃない?
    その説明 C# 混じってない?


    > 例えば、List<String>型データとArray型のデータに対して同じ機能を実行いたい場合、
    ここの文面は、List(Of String) に直したほうが良いですね。


    > メソッドからの返り値の受け渡しでも発生し、返り値の場合は、
    間違っているわけではないのですが、Return Value については、
    一般的には「戻り値」と呼びます。Microsoft の資料でもそうなっているはず。


    > 次ような解釈で問題ないでしょうか?
    概要としては間違っていないと思います。


    ただし『配列』については、まだジェネリックが無かった当時の名残で、
    特例措置として共変性を持つように設計されている点に注意して下さい。
    本来は、配列に関して共変性を認めるべきではないのですが、
    互換性の都合で、ここの仕様は変更できなかったのですね。

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      Dim x() As List(Of Button) = Nothing
      'Dim y() As List(Of Control) = x 'これは NG(コンパイルエラー)

      Dim b() As Button = {Button1}
      Dim c() As Control = b '歴史的な事情から、配列は共変となっています


      'このような場合、書き込み(反変)は本来 NG ですが、コンパイルできてしまいます。
      'そのため、実行時に ArrayTypeMismatchException 例外となってしまいます。
      c(0) = Me '本来は List(Of ) 同様、コンパイル時に検出できるのが望ましい。
    End Sub



    > ジェネリックを勉強していると、共変性と反変性ってでてきます。

    これ自体はジェネリックに限定した用語では無いですね。

    『型の共変性』とは、広い型から狭い型へ変換が常に安全に行えること。(Covariance)
    『型の反変性』とは、狭い型から広い型へ変換が常に安全に行えること。(Contravariance)
    『型の双変性』とは、広い型にも狭い型にも変換できること。(Bivariance)
    『型の不変性』とは、型を変換できないこと。(Invariance)


    上記にある「広い型」や「狭い型」ですが、ジェネリックで使われる場合、
    たとえば Double と Integer などの関係性を示しているのではありません。

    ここでは継承・派生・インターフェイス実装などの関係性、たとえば
    「TextBox のインスタンスを Control 型の引数に代入しても問題無い」とか
    「Control 型の戻り値なプロパティから TextBox のインスタンスが取得されても問題無い」
    といった関係性を指しています。


    さて、VB2010 / C# 2010 からは、ジェネリックの型パラメーターに対して
    Out / out キーワードで共変性を、In / in キーワードで反変性を
    指定できるようになりました。
    『ジェネリックを勉強していると、共変性と反変性ってでてきます。』というのも
    それが要因でしょう。

    そのため、「ジェネリックにおける共変性と反変性」について学ぶのであれば、
    In/Out キーワードについても、合わせて学んでおくべきかと思います。

    ジェネリックの共変性と反変性について書かれた下記の記事でも、In や Out が出てきますね。
    https://www.atmarkit.co.jp/fdotnet/chushin/vb2010features_01/vb2010features_01_03.html



    提示頂いたサンプルにおいて、 IEnumerable(Of T) ジェネリック型インターフェイスが登場しています。
    この型についてヘルプで調べてみると、VB2008 (.NET Framework 3.5) までは
      Public Interface IEnumerable(Of T)
       Implements IEnumerable
    という定義であると書かれていたのに対し、VB2010 (.NET Framework 4) からは
      Public Interface IEnumerable(Of Out T)
       Implements IEnumerable
    であることが分かるはずです。Out が付与されたことで、共変性を明示することができます。

    一方、反変性の In については、Action(Of T) ジェネリック型デリゲートなどで使われています。
    VB2008 付属の .NET Framework 3.5 ヘルプでは
      Public Delegate Sub Action(Of T) (obj As T)
    という定義だったものが、.NET Framework 4 からは
      Public Delegate Sub Action(Of In T)(obj As T)
    という定義に変わっていますね。



    > サンプルとして適切かどうか?

    共変性と反変性の説明のためのサンプルとして書いたのであれば、
    どの部分が共変性で、
    どの部分が反変性を示しているのかを、
    コメントで説明した方が良いかと思います。


    また、Visual Basic のサンプルとしてなら、

    '『渡したデータ型は、System.Collections.Generic.List`1[System.Int32]』
    Console.WriteLine("渡したデータ型は、{0}", myList.GetType)
    '『渡したデータ型は、System.Int32[]』
    Console.WriteLine("渡したデータ型は、{0}", myArray.GetType)

    よりも

    '『渡したデータ型は、List(Of Integer)』
    Console.WriteLine("渡したデータ型は、{0}", TypeName(myList))
    '『渡したデータ型は、Integer()』
    Console.WriteLine("渡したデータ型は、{0}", TypeName(myArray))

    の方が分かりやすいと思います。


    それと、Function の宣言途中で改行する場合、
    「As IEnumerable(Of Integer)」の前に改行を入れてはいけません。
    改行するなら As の後に加えた方が良いでしょう。もしも As の前で改行したいなら、
    その前に行継続文字「 _」が必要なはずです。 (半角の空白とアンダーバー)
違反を報告
引用返信 削除キー/
■34412 / ResNo.2)  Re[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
違反を報告
引用返信 削除キー/
■34413 / ResNo.3)  Re[3]: 共変性と反変性ってこういうこと?
□投稿者/ VBはじめました 一般人(26回)-(2019/12/06(Fri) 23:46:38)
  • アイコン魔界様いつもすみません。

    >改行無い 苦行じゃない?
    これについては、私の方が、ビックリしました。
    当然、自動で改行されるものとおもっていました。

    >その説明 C# のじゃない?
    まさしく、その通りかもしれません。
    VBでググると情報が少なく、レベルの低い私は、いまだに、.NETにおいては、VBとC#は書き方が違うだけでイコールである。の範疇を脱することが出来ていません。よって、C#との違いを理解しないまま、C#の記事をも参考にしています。そのせいもあり、余計にこんがらがっており、質問させて頂きました。
    しかし、見る人が見れば、説明が支離滅裂なのにも関わらず、参考にした文献の言語も判別でいるのにはビックリしました。

    >「広い型」や「狭い型」がDouble と Integer などの関係性を示しているのではありません。

    まさしく、そこがポイントです。ググるとString型、Objecto型。継承の関係。デリゲートとインターフェース。こんがらがるばかりです。

    Out/Inキーワードについては、Function/Actionの関係でなんとなく解った気でいますが、本質は分かっていないんだと感じています。

    VB,.NET Fraeworkのバージョンによってヘルプが変わっていることは、言われてみれば当然なことですが、それを調べられるように環境を整えているのにもビックリしました。

    >共変性と反変性の説明のためのサンプルなのだとしたら、
    どの部分が共変性で、
    投稿した後に、私も感じました。単に、IEnumerableが使えるよってサンプルになっていることを・・
    やはり、FunctionとActionで分けて考えるべきでした。

    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

    を、コピペしてみると、VisualStudioにIEnumerableを使うことを考えろと怒られました。
    ただ、魔界さんの解説がなかったら、なんで?ここで?IEnumerableなの?
    って、疑問で、こんどは、IEnumerableでググって・・・・
    すると・・次のキーワードが出てきて、また、ググって・・・・
    同じところを、グルグル回って進みません。

    一つの解説書を順に・・とも思うのですが、頭が悪い分、理解できない部分があると、ググって・・・

    当座の最終目的は、ForEach文で、検索、抽出部分をLINQで書いて簡素にしようって
    ことなのですが、山が高くて 苦労しています。

    なんだか?魔界さんに個人レッスン料払わないとだめかな?って感じています。
    教えて頂いたおかげで、なんだか、気力が湧いてきました。
    なんとか、年内には、LINQを使えるようになれたらな?って考えています。

    もうすぐ、.NET Frameworkの時代が終わって、NET5になるって記事を読んだので
    この際、Framework4の基礎的な部分だけでも・・・と思っていますが
    同期、非同期、Yield、など、エベレスト級の山がそびえているので、どこまで
    いけるやら・・・

    少し、愚痴りましたが、教えて頂いたことは、無駄にすること無い様に熟読させて
    頂いて、なんとか習得に繋げたいと考えています。

    いつも、いつも有難うございます。




違反を報告
引用返信 削除キー/
■34414 / ResNo.4)  Re[4]: 共変性と反変性ってこういうこと?
□投稿者/ VBはじめました 一般人(27回)-(2019/12/06(Fri) 23:47:11)
  • アイコン解決忘れ
解決み!
違反を報告
引用返信 削除キー/



スレッド内ページ移動 / << 0 >>

このスレッドに書きこむ

Mode/  Pass/


- Child Tree -