When programming Revit, people may notice that the Item() method has to be used in VB.NET but the get_Item() in C# for the same item access to some collection or group objects.
For example, if we’d like to get an item of the Categories group in C#, the get_Item() method has to be used this way:
// Case #1
Categories cats = CachedDoc.Settings.Categories;
Category cat = cats.get_Item(BuiltInCategory.OST_Walls);
MessageBox.Show(cat.Name);
But in VB.NET, the Item() method should be used like:
' Case #1
Dim cats As Categories = CachedDoc.Settings.Categories
' The following compiles but returns nothing.
'Dim cat As Autodesk.Revit.DB.Category = cats(BuiltInCategory.OST_Walls)
Dim cat As Autodesk.Revit.DB.Category = cats.Item(BuiltInCategory.OST_Walls)
MessageBox.Show(cat.Name)
Why is that?
It’s something deep inside the .NET Framework and the VB.NET and C# compilers. The Item of the Categories class is defined as named property actually and has two different signatures:
public override Category this[string name] { get; set; }
public virtual Category this[BuiltInCategory categoryId] { get; }
As can be seen, the first named property accepts a string argument and is read-write; the second accepts a BuiltInCategory enumerator value and is read-only.
In VB.NET, we can use the Item statement to access to them directly rather than the underlying get_Item() method.
As commented in the code, the code cats(BuiltInCategory.OST_Walls) also compiles but cannot find the right Category instance for us. The VB.NET compiler may get confused at compiling time and the .NET Framework interpreter may interpret the call as something else, likely an indexed property which only accepts a valid number.
Here is another example, accessing a particular Material item from the Materials collection.
In C#:
//Case #2
Materials mats = CachedDoc.Settings.Materials;
Material mat = mats.get_Item("Default Wall"); //Needs a transaction to be created either manually or automatically, otherwise exception occurs! :(
MessageBox.Show(mat.UniqueId);
In VB.NET:
'Case #2
Dim mats As Autodesk.Revit.DB.Materials = CachedDoc.Settings.Materials
' The following compiles but throws out an exception at runtime.
'Dim mat As Autodesk.Revit.DB.Material = mats("Default Wall")
Dim mat As Autodesk.Revit.DB.Material = mats.Item("Default Wall") 'Needs a transaction to be created either manually or automatically, otherwise exception occurs! :(
MessageBox.Show(mat.UniqueId)
This time, besides the odd behavior of the mats("Default Wall") call, Revit API also behaves quite weird. The material access wants a transaction created beforehand; otherwise, an exception would just occur!
Let’s look at one more case, retrieving a Binding item from the BindingMap group.
In C#:
// Case #3
Definition def = (from DefinitionGroup dg in CachedApp.OpenSharedParameterFile().Groups
from ExternalDefinition d in dg.Definitions
select d).First();
BindingMap map = CachedDoc.ParameterBindings;
Autodesk.Revit.DB.Binding binding = map.get_Item(def);
MessageBox.Show(binding.IsReadOnly.ToString());
In VB.NET:
' Case #3
Dim def As Autodesk.Revit.DB.Definition = (From dg As Autodesk.Revit.DB.DefinitionGroup In CachedApp.OpenSharedParameterFile().Groups _
From d As Autodesk.Revit.DB.ExternalDefinition In dg.Definitions() _
Select d).First()
Dim map As BindingMap = CachedDoc.ParameterBindings
' The following doesn't compile.
'Dim binding As Autodesk.Revit.DB.Binding = map(def)
Dim binding As Autodesk.Revit.DB.Binding = map.Item(def)
MessageBox.Show(binding.IsReadOnly.ToString())
This time, a different issue comes up. The indexed property call map(def) does not compile. The reason might be that the BindingMap does not provide another signature for the same named property for the compiler to confuse about! It is good!
So, C# is more reliable regarding this behavior through not supporting the named property feature.
All these matters will be taken care of by the RevitAddinWizard when applicable.
Recent Comments