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

型パラメータとType型の違い

環境/言語:[WindowsXP/VB2005/.NET Framework 2.0/]
分類:[.NET]

自作したジェネリックメソッドで(Of T)等として指定する型パラメータ T を
Type 型の値の代わりに使おうとするとエラーになってしまいます。
型パラメータと、System.Type 型の違いとは何なのでしょうか?


混乱してしまったきっかけは、自作クラスを作っている時です。オブジェクトのXMLシリアライズ、逆シリアライズを行うメソッドを分けて作りました。
シリアライズの方は、引数に obj as Object を渡し、その obj をシリアライズして書き出すメソッドが完成しました。逆シリアライズでは、obj を渡しても、この型が何なのか知ることができず、結局、オブジェクトの型も渡さなければなりませんでした。引数の冗長化しているようで、どうもすっきりしません。逆シリアライズのメソッドのみ、(Of T)のジェネリックメソッドになっているのも、ややこしいです。
また、逆シリアライズを行う際、一部のコードでは、型パラメータ T ではなく、Dim obj As new T とした後で、obj.GetType を代入しなければなりませんでした。

(以下、.NET TIPS のページ
 http://dobon.net/vb/dotnet/file/xmlserializer.html
 から引用)

> 'XMLファイルから読み込み、逆シリアル化する
> Dim cls As SampleClass = CType(serializer.Deserialize(fs), _
>     SampleClass)

↑この SampleClass の部分を「 T 」とするとエラー。
Dim obj As new T として obj を作った後、「obj.GetType」を指定すると成功。
この違いは何なのでしょうか。
すみません、一部自己レスです。

逆シリアル化を行うメソッドについて、ジェネリックメソッドにするのではなく、
引数に System.Type 型の変数を渡すことで、逆シリアルすることができました。
型パラメータ T は使うことなく済みました。

型パラメータと、System.Type 型の違いがわかりません。
Object.GetType で取得できるのは、System.Type 型ですよね。
では、例えば、List(Of T) の T の部分をプログラムで動的に変えたい場合、
どのように T を指定すれば良いのでしょうか。
List(Of Obj.GetType) ではエラーになります。
型パラメータはハードコーディングするしかないのでしょうか。
■No27174に返信(チョロQさんの記事)
> 型パラメータ T は使うことなく済みました。

以下のように GetType を使えば良いと言うことはありませんか?

Public Sub Test(Of TType)()
Dim b As Type = GetType(TType)
End Sub

> 型パラメータと、System.Type 型の違いがわかりません。
> Object.GetType で取得できるのは、System.Type 型ですよね。
> では、例えば、List(Of T) の T の部分をプログラムで動的に変えたい場合、
> どのように T を指定すれば良いのでしょうか。

難しく考えずに、List(Of T) と書けば良いのです。
型パラメータを TType とした場合、以下のようになります。

Public Sub Test(Of TType)()
Dim a As New List(Of TType)
End Sub


# 型パラメータは「型の別名」みたいなものと言えるかも。
# Type 型は型の情報を持つクラスですね。
2010/08/14(Sat) 19:03:44 編集(投稿者)

■No27174に返信(チョロQさんの記事)
> では、例えば、List(Of T) の T の部分をプログラムで動的に変えたい場合、
> どのように T を指定すれば良いのでしょうか。
> List(Of Obj.GetType) ではエラーになります。
> 型パラメータはハードコーディングするしかないのでしょうか。

少し、捉え違いをしていたかもしれないので補足です。
最終的に、型パラメータはどこかでハードコーディングすることになります。

中間のメソッドでは型パラメータを引きづり回せば良いですが、利用する層(上位層)のどこかでは必ずハードコーディングになります。
最上位層でも動的にしたいのであれば、それは静的な型ではないので、List(Of Object) などとするしかありません。

# 最上位層のコンパイル時点で決定できない型は、型パラメータとして利用できないことになる。
  • 題名: Re[1]: 型パラメータとType型の違い
  • 著者: 魔界の仮面弁士
  • 日時: 2010/08/14 19:08:58
  • ID: 27177
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
■No27173に返信(チョロQさんの記事)
> 型パラメータと、System.Type 型の違いとは何なのでしょうか?
どこで躓いているのかが今一つ把握できないのですが、
 Dim a As Integer
 Dim b As Type = GetType(Integer)
この場合の a と b の違いは分かりますか?

もしも型パラメータが(Of T) の場合は、これが
 Dim a As T
 Dim b As Type = GetType(T)
という事になりますし、引数が ByVal x As T であれば、
 Dim c As T = x
 Dim d As Type = x.GetType()  'ただし x IsNot Nothing
と書けます。ここまでは良いのでしょうか?


>> 'XMLファイルから読み込み、逆シリアル化する
>> Dim cls As SampleClass = CType(serializer.Deserialize(fs), _
>>     SampleClass)
> ↑この SampleClass の部分を「 T 」とするとエラー。
それは、
 Dim cls As T = CType(serializer.Deserialize(fs), T)
という事でしょうか? どのようなエラーでしたか?


> 引数の冗長化しているようで、どうもすっきりしません。
問題点が良く分かりませんが、型パラメータに付いていえば
 Sub Method1(Of T)(ByVal x As T)
であれば、引数から型が判定できるため、呼び出し時の Of を省略できます。

一方、引数などから判定できない場合:たとえば
 Function Method2(Of T)(ByVal x As Object) As T
の場合は、呼び出し時に Of で型パラメータを指定する必要があります。
ただし、これが「Class Class1(Of T)」内のメソッドであるならば、
T の型は Class1 が既に知っているため、Method2 メソッドの呼び出し時に
Of の指定を省略することができます。


> オブジェクトのXMLシリアライズ、逆シリアライズを行うメソッドを分けて作りました。
分けて作ったというのは、シリアライズ機能を SampleClass のメソッドにせず、
別クラスのメソッドとした…という意味でしょうか?

どのような実装なのか、具体的な部分が良く分かりませんでしたが、
とりあえず、型パラメータを使って書いてみました。

Public Class SampleClass
    Public Number As Integer
    Public Message As String
    Public Overrides Function ToString() As String
        Return String.Format("[{0}]{1}", Number, Message)
    End Function
End Class

Class MainClass
    Public Shared Function ToStream(Of T)(ByVal target As T) As System.IO.Stream
        Dim serializer As New System.Xml.Serialization.XmlSerializer(GetType(T))
        ToStream = New System.IO.MemoryStream()
        serializer.Serialize(ToStream, target)
        ToStream.Seek(0, IO.SeekOrigin.Begin)
    End Function

    Public Shared Function FromStream(Of T)(ByVal target As System.IO.Stream) As T
        Dim serializer As New System.Xml.Serialization.XmlSerializer(GetType(T))
        FromStream = CType(serializer.Deserialize(target), T)
    End Function

    Public Shared Sub Main()
        Dim cls1 As New SampleClass()
        cls1.Message = "テストです。"
        cls1.Number = 123

        Using stm As System.IO.MemoryStream = ToStream(cls1)
            Dim cls2 As SampleClass = FromStream(Of SampleClass)(stm)

            Console.WriteLine(cls1)
            Console.WriteLine(cls2)

            stm.Position = 0
            Console.WriteLine(New System.IO.StreamReader(stm).ReadToEnd())
        End Using
        Console.ReadKey()
    End Sub
End Class

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