-5

EDIT: Sub dump_range below shows that the address does not completely define a Range (as far as operating with its contents is concerned), something that I found surprising, and not clearly stated in MS documentation. There is something else, a "subtype". It appears that one cannot inquire about the "subtype", but only indirectly, via Count. The practical relevance of this point is that, if one defines a Sub (or Function) taking a Range as an argument, one should bear this in mind to code the Sub (something that I personally did not do) to avoid potential errors.

This question was sparked by this answer to Traversing `Cells` in a `Range`

In the code

Dim rng As Range, rng2 As Range, rng3 As Range
Set rng = Selection
Set rng2 = rng.Cells
Set rng3 = rng.Rows

(Question 1) what are the differences between objects rng, rng2, etc.?

I do not mean to get it explained only in words, but in terms of specification of what methods/properties and results are available for each, and make up the difference among the "subtypes". There should be some, as VBA works differently on them. But I do not find the specification. Actually, the Excel help for the Range.Cells Property is confusing to me: "Returns a Range object that represents the cells in the specified range." It looks to me that it should return the same object as the caller. Links to authoritative documentation will support an explanation.

One can see that there are some differences among the objects by using

Call dump_range(rng)   ' -> Range $A$1:$B$6, count = 12
Call dump_range(rng2)  ' -> Range $A$1:$B$6, count = 12
Call dump_range(rng3)  ' -> Range $A$1:$B$6, count = 6

with

Sub dump_range(ByRef rng As Range)
    Debug.Print "Range " & rng.Address & ", count = " & rng.Count
End Sub

(Question 2) I also want to know if one can test a Range object for which "subtype" it is. E.g., how would one discern the "subtypes" of objects rng and rng2, without inspecting the code? I.e., obtaining such information at run-time.

(Question 3) Is there a canonical word for "subtype"?

Community
  • 1
  • 1
  • 3
    really worth asking a question about that? There is a whole library on MSDN of Excel object model...VBA object model etc why dont you check that out –  Oct 01 '14 at 17:58
  • 1
    This is in an interesting question as the default property of a range object changes depending on how the range object is assigned: `Set rng = Sheet1.Columns(1)` is different then `Set rng = Sheet1.Range("A:A")` – JNevill Oct 01 '14 at 18:01
  • I am tempted to answer this question humorously :P – Siddharth Rout Dec 24 '14 at 07:49
  • @SiddharthRout - Be my guest, no offenses. Plus, if you think that there is a technical answer for the question, and you know it, I would love to see it. – sancho.s ReinstateMonicaCellio Dec 24 '14 at 10:07
  • awesome :) Already did :) – Siddharth Rout Dec 24 '14 at 10:14
  • @SiddharthRout - Please check my edit, while I read the new answers. Thanks. – sancho.s ReinstateMonicaCellio Dec 24 '14 at 10:20
  • 1
    @vba4all - Please read the edit. If your criterion for "a question about Excel not worth asking" is "the information to answer it is available in the documentation", then I guess that less than 0.1% of the questions are worth asking. You might take questions at random and post similar comments. Plus, I am usually thorough (but not infallible) in searching prior to posting. – sancho.s ReinstateMonicaCellio Dec 24 '14 at 10:32
  • Sancho, are you aware of using `Watch`? Like already mentioned you can use `TypeName`at runtime to see what you get. Or you can loop through their properties to do a comparision. Alternatively you can use a `Watch` to understand how they behave. I have already explained it [HERE](http://stackoverflow.com/questions/21386768/why-am-i-having-issues-assigning-a-range-to-an-array-of-variants) on how to use `Watch` – Siddharth Rout Dec 24 '14 at 10:33
  • @sancho.s please note that my comment was in regards to your [original question](http://stackoverflow.com/revisions/26147298/1) (8 revisions ago). –  Dec 24 '14 at 14:53
  • 1
    @vba4all - My comment would apply in that case too. I did not understand the rationale behind your comment. – sancho.s ReinstateMonicaCellio Dec 24 '14 at 15:16

7 Answers7

9

Question 1)

They are all the same class or object type (Range Object).

A range object is a collection of other “things” which can be cells, rows or columns etc. Each one of these is a range object.

For example, given a range R:

R.cells is a range object made up off all the cells (also range objects) i.e. {cell1, cell2, cell3, …}

R.rows is a range object made up of all the rows (also range objects) i.e. {row1, row2, row3, …} --> (similarly row1 is a range object is made up of all the cells i.e. {row1Cell1, row1Cell2, …})

And selection is equivalent to selection.cells, so in your example rng is equivalent to rng2.

Again,

Rows, cells, columns are all range objects, so they all have the same properties/methods. The reason you are getting a different count is not because they are different types of objects with different properties/methods. They are the same type of object, but not equivalent and thus some properties may be different.

To see a full list or props + methods, see here.

Question 2)

Subtype isn’t the correct word in this sense. Your examples are all the same type, there is no subtype. What they are is not equivalent. To test for “equivalence” (in the sense that you seek) you can loop through each object in the range object and recursively test equality (comparing the address strings) of every object until you get to a range object with a count of zero, see below:

 'should return "Rows"
 getRangeType(selection.Rows) 

 Function getRangeType(inputRange As Range) As String

      If (testRangeEquality(inputRange, inputRange.Rows)) Then
        getRangeType = "Rows"
      Exit Function
      End If

      If (testRangeEquality(inputRange, inputRange.columns)) Then
           getRangeType = "Columns"
           Exit Function
      End If

      If (testRangeEquality(inputRange, inputRange.Cells)) Then
          getRangeType = "Cells"
          Exit Function
      End If

 End Function


 Function testRangeLevelEquality(range1 As Range, range2 As Range) As Boolean

      If (range1.Count <> range2.Count) Then
          testRangeLevelEquality = False
          Exit Function
      End If

      IsEqual = True

      For i = 1 To range1.Count
           If (range1(i).Address <> range2(i).Address) Then
                IsEqual = False
           End If
      Next i

      testRangeLevelEquality = IsEqual

 End Function


 Function testRangeEquality(range1 As Range, range2 As Range) As Boolean

      Equality = True

      If (testRangeLevelEquality(range1, range2)) Then

           If (range1.Count = 1) Then

                Equality = True

           Else

               For i = 1 To range1.Count
                   If (testRangeEquality(range1(i), range2(i)) = False) Then
                       Equality = False
                       Exit For
                   End If
               Next i

           End If

      Else

           Equality = False

      End If

      testRangeEquality = Equality

 End Function

Question 3)

Again, there is not a notion of a subtype in this sense, just separate, nonequivalent instances of the same class. But, I think that inheritance may be the term you were looking for.

Angel Eyes
  • 302
  • 3
  • 8
  • + 1 nicely explained! – Siddharth Rout Dec 24 '14 at 07:18
  • Is there a way to explore the memory, find something in the way `rng`, `rng.Rows`, etc., are stored, and infer how VBA puts together the `Range` objects into collections? Perhaps this will show a relation between `.Rows`, etc. I tried `VarPtr` and `ObjPtr` and I could not make sense of the results (I am not familiar with their use). – sancho.s ReinstateMonicaCellio Dec 25 '14 at 09:37
  • I was aware of the difference between a class and an instance of a class, and about inheritance. I think there is no inheritance involved here, as we are dealing with only one class: `Range`. – sancho.s ReinstateMonicaCellio Dec 26 '14 at 10:22
  • I am (indirectly) trying to understand the way a `Range` is stored in memory. There should be one (`Private`) property that stores an array of (pointers to?) other `Ranges`, only for *column* or *row* "subtypes". That is how, after ruminating and considering the answers, I guess it works. i would love to see that in an answer. This one gets the closest so far, although it does not explicitly refer to actual handling with properties/storage. – sancho.s ReinstateMonicaCellio Dec 26 '14 at 11:18
  • Could you comment on where do you think inheritance is involved? – sancho.s ReinstateMonicaCellio Dec 29 '14 at 10:35
  • First off, inheritance is not involved. You were asking for the canonical word for "subtype" and I believe that inheritance was the appropriate concept despite not being applicable to this question. In this case, if reality was warped and we were indeed looking at "sub" forms of the range class, we would be looking at subclasses not subtypes because a range is a class not a type. The concept underlying subclasses is inheritance and that's why I pointed you in that direction. – Angel Eyes Dec 29 '14 at 18:23
6

What is the difference between "Sancho" and "Sancho.Hand"?

enter image description here


Why am I taking this approach?

People have already tried to explain above but seems like you are not able to comprehend what they are saying, so I am going to use a different technique. :) This is basically what "Angel Eyes" has also said in his answer.


Ok the Explanation

If you look at it in one way then there is no difference. Both have bones, muscle, blood running through them, may have hair (unless you wax!)

On the other hand yes there is a difference. "Sancho" is a collection of all body parts like "Hand", "Nose", "Eyes" etc etc. So definitely a "Hand" <> "Complete Body"

Similarly Rng (Sancho) and Rng.Rows (Sancho.Hand) are same yet they are different!

So both will have .Value, .Address, .Count properties at their disposal. But the values these properties return may or may not be the same.

What you are trying to is an incorrect comparison. You can compare

.Address to .Address
.Count to .Count
.Value To .Value '<~~ Only possible with single cell range objects

rng and rng2 are range objects. There is NO difference when it comes to Type. They will have the same properties (Not values)


Looking for authoritative documentation?

You cannot find an authoritative document which tells What is the difference between "Sancho" and "Sancho.Hand", Er, I mean What is the difference between "rng" and "rng.Cells" Because it is pure common sense. What you may find is articles on what rng or rng.Cells is. From that you have to deduce what are the differences.

Siddharth Rout
  • 147,039
  • 17
  • 206
  • 250
  • This answer is picking up, probably based on the humorous part (a la SOMeta) more than on the technical part. Perhaps this distorts the rating based on usefulness... – sancho.s ReinstateMonicaCellio Dec 25 '14 at 09:40
  • Is there a way to explore the memory, find something in the way `rng`, `rng.Rows`, etc., are stored, and infer how VBA puts together the `Range` objects into collections? Perhaps this will show a relation between `.Rows`, etc. I tried `VarPtr` and `ObjPtr` and I could not make sense of the results (I am not familiar with their use). – sancho.s ReinstateMonicaCellio Dec 25 '14 at 09:40
  • 1
    Did you explore the `watch` option? – Siddharth Rout Dec 25 '14 at 10:35
  • Yes, I did use it, as I usually do. I did not find anything shedding any more light in there. BTW, I just noticed here that Watches do not show all the properties. As mentioned, perhaps inquiring about the addresses pointed at by the various items in the various collections (`rng`, `rng.Rows`, etc.) can give some insight (as in C/C++). I do not know how to do that in VBA. – sancho.s ReinstateMonicaCellio Dec 25 '14 at 23:08
3

The difference is explained in Help:

Range.rows
Returns a Range object that represents the rows in the specified range. Read-only Range object

Compare
msgbox rng(1).address - returns A1 because its the first cell
msgbox rng3(1).address - returns A1:B1 because its the first row

if you want to find out what kind of range it is try this code

Sub TestRangeObj()
    Dim rng As Range, rng2 As Range, rng3 As Range, rng4 As Range, rng5 As Range
    Set rng = Range("A1:B6")
    Set rng2 = rng.Resize(1, 1)
    Set rng3 = rng.Rows
    Set rng4 = rng.Columns
    Set rng5 = Union(rng, Range("x45:Z50"))
    MsgBox RangeType(rng)
    MsgBox RangeType(rng2)
    MsgBox RangeType(rng3)
    MsgBox RangeType(rng4)
    MsgBox RangeType(rng5)
End Sub
Function RangeType(theRange As Range) As String
Dim nRows As Long
Dim nCols As Long
nRows = theRange(1).Rows.Count
nCols = theRange(1).Columns.Count
    If theRange.Areas.Count > 1 Then
        RangeType = "Multi-Area"
    ElseIf theRange.CountLarge = 1 Then
        RangeType = "Cell"
    ElseIf nRows = 1 And nCols > 1 Then
        RangeType = "Row"
    ElseIf nRows > 1 And nCols = 1 Then
        RangeType = "Column"
    Else
        RangeType = "2-D Range"
    End If
End Function
Charles Williams
  • 23,121
  • 5
  • 38
  • 38
2

Q3: no, there is none. When dealing with the Variant Data Type, which can contain (wrap) any Data Type, you can think of the variant's content as "subtype", e.g. "Variant containing Integer Data Type value"... Do read about / practice with VarType VBA function please.

Q2: this is meaningless (pls see below)

Q1:

let's say

Set rngX = Sheet1.Range("$A$1:$B$6")

rngX is a Range Object consisting of all the selected cell. rngX.Cells is also a Range Object consisting of all the same selected cell.

Cells property, as applied to a specified Range Object is superfluous for it returns the Range itself. The utility of the Cells property of the Range Object is realized when you do not specify the Range Object explicitly.

Let's say the active worksheet is Sheet1. Both calls below are equivalent and refer to a Range Object containing ALL THE CELLS in the worksheet Sheet1:

Sheet1.Cells
Cells

However, while the Sheet1 is active, you could refer to all the cells in some other worksheet like this:

Sheet2.Cells

That's all there is about that.

As for the Rows property of the Range Object, you are dealing with rows of cells not with individual cells. rngX.Rows returns a collection (an array) of Range Objects each of which is a single row in the rngX Range Object. In our example,

rngX.Rows

returns a collection of two Range Objects which are sub-ranges of the rngX, each representing cells in a row:

rngX.Rows(1)

is the Range Object Sheet1.Range("$A$1:$A$6"), and

rngX.Rows(2)

is the Range Object Sheet1.Range("$B$1:$B$6").

Please note that a row can consist of just one cell, as, for example, Range("A1:A100").Rows(3) is the same as Range("A3").

Aslo note that the Columns property of the Range Object works just like the Rows property, only 'vertically'.

Now, since these "sub-ranges" are Range Objects themselves, all that I've said above is applicable to them too, naturally.

  • I was aware of the information you posted. But I do not see how that answers the questions: Q1) what methods/properties make up the difference among the different objects `rng`, `rng2`,..., Q2) I think it is meaningful (and, at any rate, there should be an answer about whether it is possible, and how). For Q3 you say *NO*, but no documentation is provided. The use of `VarType` that you mention is for subtypes of variants, a different topic from what this is about (with the unfortunate coincidence that I used “subtype” as well). – sancho.s ReinstateMonicaCellio Dec 23 '14 at 04:07
  • @sancho.s Q1: no difference except as mentioned; Q2: a range is a range is a range... You might want to re-read my answer once more, please? Q3: NO; it's something like one cannot live for 1000 years and there is no documentation that can be provided for that so it is not necessarily no, right? – Gene Skuratovsky Dec 23 '14 at 11:06
  • I am (indirectly) trying to understand the way a `Range` is stored in memory. Referring to handling, `Count`, etc., it is more like **How does it do it?** than **What does it do?** There should be one (`Private`) property that stores an array of (pointers to?) other `Ranges`, only for *column* or *row* "subtypes". That is how, after ruminating and considering the answers, I guess it works. i would love to see that in an answer. – sancho.s ReinstateMonicaCellio Dec 26 '14 at 11:24
1

Interesting question. I'd never noticed that the Count property of a range changes depending on how it was declared.

I don't think anybody has addressed the Range.Item property, which is helpful here. For many objects Item just represents an objects default property, e.g., ...

? ThisWorkbook.Worksheets.Item(1).Name = ThisWorkbook.Worksheets(1).Name

...returns True because it's just two ways of saying the same thing.

For a range it looks like Item actually references the collection of "subtypes" that you seek. In other words, if a range is defined using rows, as in your rng2 variable, the Item collection refers to rows.

Although I don't see a direct way to reference the type of the Item, it looks like you can pretty easily suss it out like this:

Function GetSubType(rng As Excel.Range) As String
Dim SubType As String
If rng.Item(1).Address = rng.Cells(1).Address Then
    SubType = "cell"
ElseIf rng.Item(1).Address = rng.Rows(1).Address Then
    SubType = "row"
ElseIf rng.Item(1).Address = rng.Columns(1).Address Then
    SubType = "column"
Else
    SubType = "who knows"
End If
GetSubType = SubType
End Function

Note that if you don't set a range equivalent to Rows, etc., but just to an address as in your rng variable, the "subtype" will be Cells.

In my brief testing this works on multi-area ranges.

Of course the practical answer to avoiding the potential errors you mention is to code explicitly. Instead of Range.Count, always use Range.Cells.Count, Range.Rows.Count, Range.Areas.Count, etc.

Finally, why does MS call it Item, instead of Items, like any other collection?

Doug Glancy
  • 27,214
  • 6
  • 67
  • 115
  • `?Sancho.Count = Sancho.Hands.Count` or `?Sancho.Count = Sancho.Eyes.Count` Yup you got that right! And that is what I was trying to saying. You cannot compare apples to oranges. They both are fruits but yet they are different :) – Siddharth Rout Dec 26 '14 at 06:24
  • *I'd never noticed that the Count property of a range changes depending on how it was declared.* This was one of the kick starters, my (wrong) assumption that `.Count` gives the number of cells in the `Range`, instead of the number of items in the collection. It looks like I was not alone... – sancho.s ReinstateMonicaCellio Dec 26 '14 at 10:50
  • [Documentation on `Item`](http://msdn.microsoft.com/en-us/library/office/ff841096%28v=office.14%29.aspx) appears confusing/incosistent to me. – sancho.s ReinstateMonicaCellio Dec 26 '14 at 10:52
  • I think your `Function` may fail for 1xn or nx1 ranges. I think one should test both `Address` and `Count` to be sure. This is the test I was thinking about in my EDIT at the top. – sancho.s ReinstateMonicaCellio Dec 26 '14 at 10:57
  • @brettdj, I'm speaking of `Range.Count` in all cases. Using Excel 2010, Win 7, I can duplicate the OP's results. Have you tried running the OP's code? Does `Count` not yield different results for `rng3`? It does for me. – Doug Glancy Dec 26 '14 at 15:49
  • @sancho.s, the function is certainly incomplete. I don't actually understand your Edit#1. I think `Range.Address` will always define the range. It's `Range.Item` that might vary. Again, the bottom line to me is that you should specify whether you're counting cells or rows, etc., as stated at the end of my answer. – Doug Glancy Dec 26 '14 at 15:55
  • @SiddharthRout, absolutely. The interesting part is the variation in the Range.Item collection depending on how the range is defined. Interesting, but of no practical importance I can imagine if one is explicit about what they're counting. – Doug Glancy Dec 26 '14 at 16:07
  • @brettdj, yes, exactly. That's the whole point. It's interesting that `Range.Count` is different for the same range when it's defined based on rows. That might be what you expect, but I would have expected `Range.Count` to always return the count of cells. – Doug Glancy Dec 27 '14 at 00:11
  • @brettdj, by "no longer the same range", do you mean it has a different address? You seem to be saying that here and especially in comments to your own answer. Are you saying that `Range.Address` is sometimes different than `Range.Rows.Address`? – Doug Glancy Dec 27 '14 at 17:36
  • @brettdj, I'm still curious and interested in your answer to my last comment. – Doug Glancy Jan 01 '15 at 18:28
  • @brettdj, I can see Charles' post, thanks. It doesn't answer the very specific question I asked you about what you meant. Again, are you saying that `Range.Address` is sometimes different than `Range.Rows.Address`. It sure seems like that's what you're saying. If so, can you please give an example of that. – Doug Glancy Jan 02 '15 at 16:18
  • @brettdj, Ok then. I, of course, agree with those basic facts. (FYI, as far as I can tell, so did the OP before they ever asked their question.) I still think the OP raised an interesting question, although not a practical one. And I think this post answers that question, i.e., how to distinguish between two ranges with the same address, when for example, one was declared with `rng.Rows` and another with `rng.Columns`. – Doug Glancy Jan 02 '15 at 16:59
  • @brettdj, you say "is expected" as if we've all memorized the many vagaries of VBA :). Before this post I would have expected rng.Count to always equal rng.Cells.Count even if rng is declared as SourceRange.Rows.Count. Now I understand that the Item Collection reflects the definition of the range and isn't just a collection of cells. I agree that it's a non-issue, or should be, because one should always be explicit, and code for rng.Rows.Count, rng.Cells.Count, etc., no matter how the range was defined. And just to be clear, you "still have access to Rows" or cells, etc., in any range. – Doug Glancy Jan 03 '15 at 01:20
0

To answer your questions, There is a way to check the type of a variable at run-time, using the VarType function.

However, in your case:

Dim rng As Range
Set rng = Range("A1:B6")
MsgBox TypeName(rng) & " : " & VarType(rng) & " : " & rng.Count' Range : 8204 : 12
MsgBox TypeName(rng.Rows) & " : " & VarType(rng.Rows) & " : " & rng.Rows.Count' Range : 8204 : 6
MsgBox TypeName(rng.Columns) & " : " & VarType(rng.Columns) & " : " & rng.Columns.Count' Range : 8204 : 2
MsgBox TypeName(rng.Cells) & " : " & VarType(rng.Cells) & " : " & rng.Cells.Count ' Range : 8204 : 12

As you can see, Excel implements all three variable as the same type.

Therefore, I deduce that there is an private state to the Range class that determines whether it is in the context of Cells,Rows or Columns, with Cells being the default

Unfortunately, non of the properties of the Range class expose this kind of state

Uri Goren
  • 13,386
  • 6
  • 58
  • 110
  • you can check the 'state' with something like Application.Intersect(r, r.entirerow).Count = r.entirerow.Count – Seb Dec 22 '14 at 17:03
  • As I understand, your answers to my specific questions would be: Q1) you are conjecturing there is a "hidden field", the same as I am, but you cannot provide specs or links to documentation, Q2) you are conjecturing it is not possible, the same I found so far, Q3) ?. Am I guessing right? – sancho.s ReinstateMonicaCellio Dec 23 '14 at 03:34
-2

The short answer is that all three ranges are the same. You would never use Range.Cells except to refer to a cell within that range since .Cells returns all cells in that range. And for Range.Rows it is the same. A row in Excel is simply the collection of cells in the row of the range.

Mr. Mascaro
  • 2,693
  • 1
  • 11
  • 18
  • They aren't the same though. Take this example: `Set rng = Sheet1.Columns(1)` and `Set rng2 = Sheet1.Range("A:A")` If you do this then `rng.count` will be `1` and `rng2.count` will be `1048576` – JNevill Oct 01 '14 at 18:05
  • But the address of both is the same and you would assign values the same way and literally every other way you would use the range is the same. – Mr. Mascaro Oct 01 '14 at 18:14
  • This is true, however the default property of the range object (when it is an object as opposed to a single cell/scalar) is different. This is where the confusion lies. In my example above, if I try: `For Each thing In rng` I will get a single iteration, where if I try: `For Each thing in rng2` I will iterate through each cell. It suggests that the way one sets the Range object determines it's default property. – JNevill Oct 01 '14 at 18:18
  • That's why I never do that. It's poor coding practice to use a form that relies on reading an entire module to debug the results. Even in all of the MS documentation you'll never see `For Each object In Range`. You always see something like 'For Each object In Range.Cells`. It's a better coding practice. – Mr. Mascaro Oct 01 '14 at 18:23
  • I agree. You should always be explicit instead of relying on the parser to figure out what you wanted. The question is still interesting though. – JNevill Oct 01 '14 at 18:30
  • @JNevill, actually I just finished debugging a few modules of a custom ERP built on Excel/SQL and not following the proper form was a killer. Took 10 times longer than it should have. – Mr. Mascaro Oct 01 '14 at 18:36
  • 1
    @jbarker2160 - Regarding my specific questions, I would disagree in that they are the same. If they behave differently (e.g., they produce different outputs, using the same methods), they are not the same. The VBA interpreter can tell the difference. How can we? Good practices are a different topic. – sancho.s ReinstateMonicaCellio Oct 01 '14 at 22:10
  • @jbarker2160 - Please see updated question, it better justifies why objects are different, even if they cover the same address (which is different from being the same object). – sancho.s ReinstateMonicaCellio Oct 07 '14 at 05:28