I'm reading over this query, and I came upon a line where I don't understand heres the line
[FETT List]![FETT Search]
FETT List is a table
FETT Search is a column in FETT List
Can someone explain what the exclamation mark means?
Thanks
Well, you learn something new every day!
I had originally planned to explain that if you'd said the reference was [Forms]![FETT List]![FETT Search], then it would be easy to explain, as a reference to the [FETT Search] control on the [FETT List] form. But without a parent collection (either Reports of Forms), it doesn't look like a valid reference in any context within a SQL statement.
But then I thought to test it, and discovered (to my surprise) that this SQL statement is treated as valid in an Access form:
SELECT [tblCustomer]![LastName] AS LastName
FROM tblCustomer;
In Access, that is 100% equivalent to this SQL statement:
SELECT tblCustomer.LastName
FROM tblCustomer;
…so I don't understand why anyone would write it, except if they forgot the context (or never understood it in the first place). It could be a case of aliasing gone wrong, but it's not what I consider good form.
Now, the long answer to the general question of ! (bang) vs. . (dot):
In general, in Access, the bang operator delineates the default collection of an object and its items. The dot operator delineates an object and its methods, properties and members.
That is for Access, and applies to Access objects and the object model for Access.
But you also use SQL in Access, and so you also have TableName.FieldName in SQL, where the dot operator separates an item in a default collection. TableName.FieldName could be considered to be short for TableName.Fields("FieldName"), as you find with Forms!MyForm!MyControl being equivalent to Forms!MyForm.Controls("MyControl"). But this rule doesn't apply in SQL -- TableName.Fields("FieldName") is not valid SQL, only TableName.FieldName is.
So, you have to keep straight which paradigm is controlling the namespace you're working in, i.e., whether it's an Access namespace or a SQL namespace.
Forms!MyForm is also equivalent to Forms.Item("MyForm"), so the ultra-long form would be Forms.Items("MyForm").Controls("MyControl"). Note how the bang operator is a shortcut for the longer form version with the dot operator, so the bang operator is quite frequently used in preference to the dot operator. Note also that the longer form ends up being used when you need to refer to an item whose name is stored in a variable, which is not possible with the bang operator:
Dim strForm As String
strForm = "MyForm"
' This is OK
Debug.Print Forms(strForm).Controls.Count
' This is not
Debug.Print Forms!strForm.Controls.Count
Also, in VBA code, Microsoft has engineered things to obfuscate this distinction in Forms and Reports, where it used to be that Me!MyFavoriteControl was legal as a control reference, and Me.MyFavoriteControl would have been legal only as a reference to a custom property (or module-level variable, which would be member of the object). You could also unwisely name a function or sub "MyFavoriteControl" and it could be referred to with the dot operator.
But with the introduction of VBA, MS introduced implicitly-created (and maintained) hidden property wrappers around all controls so that you could use the dot operator. This had one huge advantage, and that is compile-time checking of control references. That is, if you type Me.MyFavoriteControl and there is no control by that name and no other member of any kind with that name within the form/report's namespace, then you would get a compile-time error (indeed, you'd be informed of the error as soon as you left the line of code where you made the error). So, if you had this code:
Debug.Print Me.Control1
... and you renamed Control1 to be MyControl, you'd get an error the next time you compiled the code.
What could be the downside of compile-time checking? Well, several things:
code becomes harder for the programmer to understand on sight. In the past, Me!Reference meant an item in the default collection of a form/report (which is a union of the Fields and Controls collections). But Me.Reference could be a control or a field or a custom property or a public module-level variable or a public sub/function or, or, or... So, it sacrifices immediate code comprehensibility.
you are depending on implicit behavior of VBA and its compilation. While this is usually an OK thing to do (particularly if you take good care of your code), VBA compilation is very complex and subject to corruption. Over the years, experienced developers have reported that using the dot operator makes code more subject to corruption, since it adds another layer of hidden code that can get out of synch with the parts of the the application that you can alter explicitly.
since you can't control those implicit property wrappers, when they go wrong, you have to recreate your module-bearing object from scratch (usually SaveAsText is sufficient to clear the corruption without losing anything).
So, many experienced developers (myself included) do not use the dot operator for controls on forms/reports.
It's not such a big sacrifice as some may think if you use a standard set of naming conventions. For instance, with bound controls on forms, a let them use the default names (i.e., the name of the field the control is bound to). If I don't refer to the control in code, I never change its name. But the first time I refer to it in code, I change its name so that the control name is distinct from the name of the field it is bound to (this disambiguation is crucial in certain contexts). So, a textbox called MyField becomes txtMyField at the time I decide to refer to it in code. The only time I'd ever change the field name after code is written is if I somehow decided that the field was misnamed. In that case, it's easy enough to do a Find/Replace.
Some argue that they can't give up the Intellisense, but it's not true that you entirely give it up when you use the bang operator. Yes, you give up the "really intelligent" Intellisense, i.e., the version that limits the Intellisense list to the methods/properties/members of the selected object, but I don't need it for that -- I need Intellisense to save keystrokes, and with Ctrl+SPACEBAR you get a full Intellisense list that autocompletes just like the context-specific Intellisense, and can then short-circuit the typing.
Another area of dot/bang confusion is with DAO recordsets in VBA code, in which you use the dot operator for the SQL that you use to open your recordset and the bang operator to refer to fields in the resulting recordset:
Dim rs As DAO.Recordset
Set rs = CurrentDB.OpenRecordset("SELECT MyTable.MyField FROM MyTable;")
rs.MoveFirst
Debug.Print rs!MyField
rs.Close
Set rs = Nothing
If you keep in mind which namespace you're working in, this is not so confusing -- the dot is used in the SQL statement and the bang in the DAO code.
So, to summarize:
in SQL, you use the dot operator for fields in tables.
in forms and reports, you use the bang operator for controls and the dot operator for properties/methods (though you can also use the dot operator, but it's not necessarily advisable).
in VBA code, references to controls on forms and reports may use either dot or bang, though the dot may be prone to possible code corruption.
in SQL, you may see the bang operator used, but only if there is a reference to a control on an Access form or report, of the form "Form!FormName!ControlName" or "Report!ReportName!ControlName".
in VBA code working with DAO recordsets, you may see both the dot and bang operator, the former in defining the SQL that is used to open the recordset, and the latter to refer to fields in the resulting recordset once it is open.
Is that complicated enough for you?
Generally you see this in MS Access code (for the exclamation mark, a period for SQL server). You can refer to a column by table.column or if you give the table an alias, then by alias.column. You might do this if you want to be specific when using joins, or you may have to do it when two (or more) tables in a query/join have the same column name in each table.
I think that the esclamation mark is only a conventional separator.
In Oracle PL/SQL you use dot:
[FETT List].[FETT Search]
Any other clues?!
Related
In my current project in Access VBA, I created a window which works like a console and now I am trying to add a possibility to display any public variable. It should work like:
Show_var [variable_name]
So it should be like in the direct window where i can type:
? pVar.variable_A
I found out that using
Application.VBE.ActiveVBProject.VBComponents(11).CodeModule.CountOfLines
I can display the number of lines within a module or form so I thought perhaps I could somehow find the variables there, cycle through them and when the correct one is found, its value can be shown. OFC I could make a Select Case Statement where all variables are included but that is not the way I want to do it because it is complicated and must be changed every time update my public variable list.
There are so many problems that I think you are out of luck. Let me just list some of them:
There is no way to get a list of all variables - that would mean you would need access to the internal symbol table.
As a consequence, you would have to read the code (CodeModule lets you read the code of a module), and write an own parser to fetch all declarations (hopefully you use Option Explicit)
AFAIK, the is no method that can access the content of a variable via it's name.
Even if there would be any such method: What would you do with different data types, arrays and especially with objects.
If a user runs into problems, often it is a runtime error. If you don't handle that errors with some kind of error handler, the only option if a user cannot enter the debugger is to press "End" - which would destroy the content of all variables.
You have to deal with scope of variables, you can have several variables with the same name and you will likely have lots of variables that are out of scope in the moment you want to dump them.
I'm not successful in using the Bang (!) operator without it's argument being hardcoded, i.e., Me.VBProject.References!Excel. In this example, the Excel reference is hardcoded.
Out of frustration I've tried all permutations I can think of in an attempt to utilize it:
[Me.VBProject.References!(str)]
[Me.VBProject.References! & (str)]
["Me.VBProject.References!" & str]
["Me.VBProject.References!" & (str)]
and many more with parens added to ensure proper pre-evaluation including the longhand Application.evaluate method. Nada!
No, you can't do that.
The bang operator is just a shortcut for calling the default member of the object, and passing the text after the bang as a string to the first parameter of the default member:
The bang notation:
Me.VBProject.References!Excel
is exactly equivalent to:
Me.VBProject.References.Item("Excel")
and, because it is the default member, you can omit the Item function call:
Me.VBProject.References("Excel")
So, to use your (really badly named) variable str:
str = "Excel"
Debug.Print Me.VBProject.References.Item(str).Name
This is an X-Y problem.
Bang notation is a means to an end.
It's one of the tools made available to you, to retrieve an item from a collection.
Nothing more, nothing less.
What you want isn't to use the bang operator with a variable.
What you want is to retrieve an item from a collection using a variable.
Collection types have a default member, typically named Item. Default members can be specified explicitly, or implicitly accessed:
Dim foo As New Collection
foo.Add Item:=42, Key:="test"
Debug.Print foo.Item("test") 'explicit reference to the default member
Debug.Print foo("test") 'implicit reference to the same default member
The bang operator is just another way to make an implicit call to the collection's default member:
Debug.Print foo!test
All 3 Debug.Print statements above will call the foo.Item default member to output the 42 associated with the key "test".
Square brackets
As you can see, what comes immediately after the ! operator is really a string literal. Because a string literal can contain whitespace, the VB6/VBA parser needed a way to support them.
That's what the [square brackets] are for:
foo.Add 72, "spaces in the key"
Debug.Print foo![spaces in the key]
When they're not delimiting a string literal for bang notation, square brackets are usually1 interpreted as a run-time expression for the host application to evaluate. For example this is legal (though questionably advisable) in Excel VBA:
Debug.Print [A1]
The VBA parser identifies a bracketed expression and defers its evaluation to the host application - here Excel; at run-time, the instruction ultimately equates to:
Debug.Print ActiveSheet.Range("A1").Value
If you don't believe the evaluation of a bracketed expression is deferred to the host application, consider what needs to happen for this instruction to print 4:
Debug.Print [Sum(2,2)]
Therefore, every single one of the attempts/permutations in your post missed the mark and made Excel try to evaluate an expression that only VBA would be able to resolve, because Me.VBProject.References means absolutely nothing to Excel.
Square-bracket expressions should usually be avoided, because you lose compile-time checks and any error can only be caught at run-time.
1Usually, because they can also be used in some identifiers,
for example in Enum types, to make a [_hidden] enum member.
Bottom Line
Bang notation is a way to retrieve an item from a collection by leveraging default members and making string literals look like an identifier. You can't make it work without "hard-coding" the string literal, because it requires a string literal.
If you want to parameterize the collection retrieval, you can't use the bang operator.
It's useful for typing the code faster. If you don't know exactly how it works and what it does for you though, it's a double-edged blade that hides what's really going on and ultimately makes the code harder to read and understand. Code shouldn't be written just to be run. Code should be written to be read and understood.
Note: Bang notation isn't actually only for collections. It actually passes its argument as a string literal to the first parameter of anything that has a default member. I would strongly advise avoiding it for anything other than a collection class though (e.g. Collection.Item, Workbooks.Item, Worksheets.Item, Recordset.Fields, etc.), for the sake of future maintainers' sanity.
Edited to answer my own question. This appears to be a LINQ/VB bug.
A simple LINQ query returning an anomymous type will sometimes change the field names specified in the query so as to capitalize them. Perhaps passing the result of the query as a parameter to a method call:
someThing.someMethod(From someStuff In stuffList _
Select text = someStuff.Name(), _
value = someStuff.Id
)
where someMethod has signature
Public Sub someMethod(ByVal list As IEnumerable(Of Object))
If you step into the execution of someMethod, and then examine the value of list in quickwatch, you may or see the field names as "text"&"value" or "Text"&"Value".
LINQ queries should not be changing the field names as specified in the query, so the correct behavior is fieldnames "text"&"value". Yet production builds of our application have the incorrect capitalization behavior (which can be determined indirectly), and debug builds have shown it both happening both ways at different times and/or for different developers' machines.
I've looked high & low for some feature of LINQ which controls this behavior, but now am virtually certain it is a bug. (msdn forum thread, MS Connect bug page)
This is likely to only cause a problem if you are using reflection, such as type.getfield() such as in
listItem = list.ElementAt(index)
itemTextField = listItem.GetType().GetField("text")
itemText = CType(itemTextField.GetValue(listItem),String)
If this happens to you, the workaround is to use overload of GetField with bindingflags to make it case-insensitive:
itemTextField = listItem.GetType().GetField("text", BindingFlags.IgnoreCase)
It must be pretty rare to encounter this bug, but maybe the next person will spend less time scratching their head if they find this info here.
=========original post===========
Getting different behavior in my debug build environment than in my coworkers' and our production envirnonment, relating to LINQ and reflection...
While running debug build of legacy code, the following code
Dim objectType As Type = obj.GetType()
Dim field As FieldInfo = objectType.GetField(name)
Dim prop As PropertyInfo = objectType.GetProperty(name)
results in Nothing for field & prop.
The value of obj is passed down from above and is the result of a LINQ query (it is a single element of the list generated by the query):
From bpt In CustomBProcessTypes Select text = bpt.Name(), value = bpt.Id
The value of name is also passed from above and is "Text" (note capitalization).
I can examine obj in the debugger and confirm that the fieldnames of the object created by the LINQ query are 'text' and 'value' (note lack of capitalization) which is what I would expect.
So failure to find the field by the capitalized name makes sense. However, our production builds and my coworkers builds do not have this problem.
Because calls to type.getfield(string) are expressly cas-sensitive, the only thing I can think of at this point is there must be some configuration of LINQ relating to auto-capitalization of column/fieldnames, and my environment is not set up the same as the others.
Using visual studio 2012. I don't know much of anything about LINQ, per se.
Anyone have any idea what could be going on here?
(NOTE: if I can get an opportunity, I'll have a coworker step through the relevant code and see if in their environment the object created by the linq query ends up with capitalized field names)
EDIT: I verified with a coworker in his debug build: his LINQ query creates a list of objects with field names "Text" and "Value", but on in my environment the LINQ query ends up with field names "text" and "value". The code is the same, but there must be something about how LINQ is configured in my environment which fails to auto-capitalize those field names, but which happens on their machines and in our production environment.
I suppose it is possible that some compiler settings are resulting in different capitalization. Normally this would make no difference because VB.NET is a case-insensitive language so obj.Text and obj.text both work just as well. But to use case insensitivity in reflection lookups, you need to specify it by including BindingFlags.IgnoreCase in the second parameter of GetField or GetProperty:
Dim field As FieldInfo = objectType.GetField(name,
BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.IgnoreCase)
I'm confused as to where name is coming from, though. Some other code is getting the field name from reflection on the query? I didn't see where this was explained in your question.
I have answered my own question (insofar as is possible). Boils down to a bug in LINQ/vb.net.
Fully explained at top of original post (edited in). Hope this saves someone time in the future.
I'm writing some code in Visual Basic 6 and I have noticed that I don't even need to declare variables for things to work.
The following (explicit declaration):
Dim foo As String
foo = "Bar"
Seems to work just as well as this (implicit declaration):
Dim foo
foo = "Bar"
Or this (no declaration):
foo = "Bar"
I know in C# I need to declare a variable before I use it, and that implicit and explicit declarations are both acceptable. I also know that in Python, you don't declare your variables at all before you use them.
In regards to Visual Basic 6 (and by extension VBA) which is proper?
Thanks
It's a good HABIT.
There is a VB option called Option Explicit. With that set to ON, then VB forces you to declare a variable before you use it: no more
foo = "Bar"
That helps with mistyping the variable name later in your code... without that, you can typso the variable name, your program compiles but won't work, and it's HARD to dig that out.
In Tools/Options, Editor tab, check the Require Variable Declaration checkbox. This will automatically add Option Explicit to every new code module.
I would say this is more than a best practice; I think of it as a requirement for programmer sanity. The setting is persistent; once set, it stays enabled. Microsoft made it an option because some older versions of VB didn't have the feature, which also explains why it was disabled by default.
Should I explicitly declare my variables in VB6?
Yes. Why?
Not just because it is a good habit or it is a must but because of only one main reason which I have mentioned in this post as well.
VB defaults the variable to being type Variant. A Variant type
variable can hold any kind of data from strings, to integers, to long
integers, to dates, to currency etc. By default “Variants” are the
“slowest” type of variables.
AND
As I mentioned earlier, If you do not specify the type of the
variable, VB defaults the variable to being type Variant. And you
wouldn’t want that as it would slow down your code as the VB Compiler
takes time to decide on what kind of variable you are using. Variants
should also be avoided as they are responsible for causing possible
“Type Mismatch Errors”.
Topic: To ‘Err’ is Human (See Point 3)
Link: http://siddharthrout.wordpress.com/2011/08/01/to-err-is-human/
The above link also covers other parts related to coding that one can/should take care of.
HTH
I highly reccomend that you always declare your variables. This can be forced by setting Option Explicit in each code module. You can let VB6 do that automatically for you by going to Tools->Options, in the Editor tab check Require variable declaration.
If you don't use Option Explicit, then a variable will be automatically created for you each time you reference a previously unknown variable name. This is a very dangerous behavior, because if you mistype a variable name, an empty variable will be created for you, causing unexpected behavior of your code.
You don't have to declare the type of your variables but I would also recommend that you do that. The default type of a variable is Variant, which has a small performance overhead and create some problems if you are creating COM objects for use by C++ or C# (if anybody does that anymore).
In cases where Using can't be used because IDisposable is not implemented, is the following code an OK practice for With/End With? Would this cause a memory leak or would it be better to set an instance variable and then set it to nothing?
With New System.IO.FileInfo(sFileName)
' Do some work
End With
The With keyword has nothing to do with IDisposable or the Using keyword. It is just a handy short-cut to avoid having to type the name of the object reference.
With New System.IO.FileInfo(sfilename)
Console.WriteLine(.Length) '' note the dot without an object reference
'' etc..
End With
Which is the same as:
Dim info = New System.IO.FileInfo(sfilename)
Console.WriteLine(info.Length)
'' etc..
Since FileInfo doesn't implement IDisposable, you otherwise do not have any use for Using. Do avoid assuming that With takes care of disposing the object reference used in the With statement. It doesn't. Does kinda make sense that it would but a good 15+ years of it being around stops the VB.NET team from altering its behavior so dramatically. It was never more than a short-cut to type less code. Featured pretty heavily in the "Why doesn't C# has the with keyword" questions of yore. Hot potato in the early days of C# but it hasn't been for a while now.
With has nothing to do with memory or resource usage. It's just a shorthand notation.
With
is more than just shorthand notation. If you setting a large number of properties on an object with can slightly improve performance per MSDN, "If the qualification path to the object is long, using With...End With can improve your performance." (See Remarks on http://msdn.microsoft.com/en-us/library/wc500chb.aspx ) The reason it does this this is that a with block will only have to get the object reference one time and then it reuses that object reference for each subsequent call. If you explicitly list the object each time the runtime has to get the object reference multiple times. It's not a big difference but it can be important in some cases.