tmighty
tmighty

Reputation: 11399

Writing extension for Drawing.Rectangle

Drawing.Rectangle has the method

Dim r As Drawing.Rectangle = Drawing.Rectangle.FromLTRB(...)

I would like to write an extension method similar to that function. The extension method would create a rectangle that would contain all given points.

I have written such a method, but "Rectangle" doesn't notice that it has this new method.

When I try to access the method, Intellisense won't find it.

I have declared the extension in a module like this:

<Extension()>
Public Function FromPoints(ByVal points As List(Of Drawing.Point)) As Drawing.Rectangle

    Dim leastX As Integer = 0
    Dim maxX As Integer = 0
    Dim leastY As Integer = 0
    Dim maxY As Integer = 0

    Dim bDone As Boolean = False

    For Each p As Point In points
        If Not bDone Then
            bDone = True
            leastX = p.X
            maxX = p.X
            leastY = p.Y
            maxY = p.Y
        Else
            If p.X < leastX Then
                leastX = p.X
            End If
            If p.X > maxX Then
                maxX = p.X
            End If
            If p.Y < leastY Then
                leastY = p.Y
            End If
            If p.Y > maxY Then
                maxY = p.Y
            End If
        End If
    Next

    Return Drawing.Rectangle.FromLTRB(leastX, leastY, maxX, maxY)

End Function

Did I miss anything to make it work?

Upvotes: 1

Views: 111

Answers (1)

jmcilhinney
jmcilhinney

Reputation: 54427

The FromLTRB method you cite is Shared, i.e. it is a member of the Rectangle type but not of a Rectangle instance. Notice that you call it on Rectangle but not on A Rectangle.

Extension methods can only be used as though they are instance members. You can't write an extension method that you call on the Rectangle type but only that you call on a Rectangle instance. The instance that you call it on is actually represented by the first parameter. Your extension method would have to look like this:

Imports System.Runtime.CompilerServices

Public Module RectangleExtensions

    <Extension>
    Public Function FromPoints(source As Rectangle, points As List(Of Point)) As Rectangle
        '...
    End Function

End Module

and then you would have to call it like this:

Dim rect As Rectangle
Dim points As New List(Of Point)

'...

Dim newRect = rect.FromPoints(points)

That's not exactly ideal because you have to have a Rectangle to begin with but there's no way around that because an extension method requires an instance of the type being extended to be called on.

By the way, I would tend to write that method like this:

Imports System.Runtime.CompilerServices

Public Module RectangleExtensions

    <Extension>
    Public Function FromPoints(source As Rectangle, points As IEnumerable(Of Point)) As Rectangle
        Dim pointArray = points.ToArray()
        Dim allX = pointArray.Select(Function(p) p.X).ToArray()
        Dim allY = pointArray.Select(Function(p) p.Y).ToArray()

        Return Rectangle.FromLTRB(allX.Min(),
                                  allY.Min(),
                                  allX.Max(),
                                  allY.Max())
    End Function

End Module

Using IEnumerable(Of Point) makes the method more flexible and using LINQ makes it more succinct. The LINQ actually makes the performance worse but you'd have to have a significantly large list of Points for it to make any noticeable difference.

EDIT:

I should point out explicitly that the fact that an extension method requires an instance and this scenario doesn't require an instance prior to calling the method means that you shouldn't be using an extension method in this case. Just write a method that takes a list of Points and returns a Rectangle and put it somewhere appropriate:

Public Function RectangleFromPoints(points As IEnumerable(Of Point)) As Rectangle
    Dim pointArray = points.ToArray()
    Dim allX = pointArray.Select(Function(p) p.X).ToArray()
    Dim allY = pointArray.Select(Function(p) p.Y).ToArray()

    Return Rectangle.FromLTRB(allX.Min(),
                              allY.Min(),
                              allX.Max(),
                              allY.Max())
End Function

Upvotes: 3

Related Questions