WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

List(Of T) と Count プロパティ

by WebSurfer 12. December 2015 15:09

VB.NET の場合、定義されているプロパティが同じクラスで定義されている(もしくは継承したクラスで定義されている)同名のメソッドを隠してしまうという話を、List(Of T) と Count プロパティを例にとって説明します。

例として以下のコードを考えます。

Dim e As IEnumerable(Of Int32) = Enumerable.Range(1, 10)

' これは OK。拡張メソッド Count を呼び出す
Console.WriteLine(e.Count(Function(n) n >= 5))

Dim x As List(Of Int32) = e.ToList

' ビルドエラー「Public ReadOnly Property Count As Integer' には引
' 数がないため、戻り値の型をインデックス化できません。」になる
Console.WriteLine(x.Count(Function(n) n >= 5))

'明示的に拡張メソッドを呼び出せば OK
Console.WriteLine(Enumerable.Count(x, Function(n) n >= 5))

e は IEnumerable(Of T) 型で Count という名前では拡張メソッドの Count のみを実装しています。(同名のプロティは実装されていません)

なので、e.Count(Function(n) n >= 5) で拡張メソッドの Count を呼び出せています。

一方、x は IEnumerable(Of T) から ToList メソッド を使って作成した List(Of T) 型になります。List(Of T) 型には Count プロパティCount 拡張メソッド(Enumerable.Count のオーバーロード)の両方が実装されています。

VB.NET の場合、List(Of T) 型に実装されている Count プロパティによって拡張メソッドの Count が隠されてしまい、コンパイラは Count プロパティが呼ばれていると判断して上のソースにコメントしたようなビルドエラーになります。

これは VB.NET のコンパイラの問題で、同名のプロパティとメソッドを区別できないところからきているようです。

そのことを書いた Micosoft の公式文書は見つかりませんでしたが、MSDN フォーラムの記事で、Matt Warren - MSFT さんが、

"VB compiler is matching the lists Count property instead of the Enumerable.Count() extension method and the Count property is blocking the visibility of the extension method's other signature."

Timothy Ng MSFT さんが、

"in VB, properties shadow methods (and thus, extension methods) by name."

と書いた説明で自分的には納得しています。

そのことは List(Of T) と Count プロパティ / メソッドに限った話ではなく、以下のような自作のクラスで定義した同名のプロパティとメソッドでも再現できます。

Module Module1
    Sub Main()
        Dim sample As SampleClass = New SampleClass()

        ' SampleClass の Name プロパティが呼ばれる
        Console.WriteLine(sample.Name)

        ' 同じく SampleClass の Name プロパティが呼ばれる。
        ' VB.NET の ( ) は、C# の [ ] と同様に、配列の要素にアク
        ' セスするので、sample.Name(1) は "Property called." の 
        ' 2 文字目の 'r' となる。
        Console.WriteLine(sample.Name(1))

        ' 同じく SampleClass の Name プロパティが呼ばれる。
        ' ( ) は文字列(配列)"Property called." の要素にアクセ
        ' スする。引数 "abc" は Int32 型に変換できないのでビルド
        ' エラーになる。
        Console.WriteLine(sample.Name("abc"))
    End Sub
End Module

Public Class SampleClass
    Inherits SampleBaseClass
    ' Shadows キーワードを付けないと以下の警告が出るが、付けな
    ' くても結局は shadow される。
    ' 「property 'Name' は、function 'Name' とベース class 
    ' 'SampleBaseClass' で競合しています。'Shadows' として宣言
    ' されなければなりません。}
    Public ReadOnly Property Name() As String
        Get
            Return "Property called."
        End Get
    End Property
End Class

Public Class SampleBaseClass
    Public Function Name(ByVal i As Int32) As String
        Return "Instanse Method called."
    End Function
End Class

Public Module MyExtension
    <System.Runtime.CompilerServices.Extension()>
    Public Function Name(ByVal x As SampleBaseClass, _ 
                         ByVal s As String) As String
        Return "Extension Method called."
    End Function
End Module

なお、C# の場合はこのような問題なく、以下のコードでいずれも期待した通りの結果 6 を取得できます。

IEnumerable<int> e = Enumerable.Range(1, 10);

Console.WriteLine(e.Count(n => n >= 5));

List<int> x = e.ToList();

Console.WriteLine(x.Count(n => n >= 5));
Console.WriteLine(Enumerable.Count(x, n => n >= 5));

Tags: ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  January 2020  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar