Yes, you are expected to cache field indices to improve performance. Single field index lookup might take just few CPU ticks, but in tight loops, it is advisable to avoid doing so repeatedly.
This is especially true when working in .NET since the additional COM interop call marshalling will be more costly than the FindField method itself.
You can, however, have best of both worlds - precache field indices in a Dictionary<string, int>, then doing the lookup purely in .NET, which is faster than FindField. I have a class TableFields which has methods like object GetValue(IRowBuffer rowBuffer, string fieldName): it internally uses the dictionary (it caches the field index if not already cached for the field) and performance is very good. This way I do not do excessive field lookups using IFields.FindField and do not have methods cluttered with lot of local variables containing field indices.
EDIT: example of this approach:
public class TableFields
{
private readonly IDictionary<string, int> _fieldIndices = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
private readonly string _tableName; // for error reporting
// even though these two vars are used in two methods only, they are declared here
// to minimize excessive memory allocations
private int _index;
private object _value;
///<summary>
/// Constructor.
///</summary>
///<param name="tableName">Table name. Used only for exception reporting..</param>
///<exception cref="ArgumentNullException">if <paramref name="tableName"/> is <b>null</b>.</exception>
public TableFields(string tableName)
{
if (tableName == null) throw new ArgumentNullException("tableName");
_tableName = tableName;
}
public object GetValueAsObject(IRowBuffer rowBuffer, string fieldName)
{
return GetValueAsObject(rowBuffer, fieldName, null);
}
public int GetValue(IRowBuffer rowBuffer, string fieldName, int defaultValue)
{
return Convert.ToInt32(GetValueAsObject(rowBuffer, fieldName, defaultValue));
}
... convenience methods for other field types
public object GetValueAsObject(IRowBuffer rowBuffer, string fieldName, object defaultValue)
{
if (_fieldIndices.ContainsKey(fieldName))
{
_index = _fieldIndices[fieldName];
}
else
{
_index = rowBuffer.Fields.FindField(fieldName);
_fieldIndices.Add(fieldName, _index);
}
if (_index == -1)
{
throw new TableFieldNotFoundException(
string.Format("{0}.{1}", _tableName, fieldName),
string.Format("Field named {0} was not found in table {1}", fieldName, _tableName));
}
_value = rowBuffer.get_Value(_index);
if ((_value == null) || (_value == DBNull.Value) || (_value.ToString().Length == 0))
{
return defaultValue;
}
return _value;
}
public void SetValue(IRowBuffer rowBuffer, string fieldName, object newValue)
{
if (_fieldIndices.ContainsKey(fieldName))
{
_index = _fieldIndices[fieldName];
}
else
{
_index = rowBuffer.Fields.FindField(fieldName);
_fieldIndices.Add(fieldName, _index);
}
if (_index == -1)
{
throw new TableFieldNotFoundException(
string.Format("{0}.{1}", _tableName, fieldName),
string.Format("Field named {0} was not found in table {1}", fieldName, _tableName));
}
rowBuffer.set_Value(_index, newValue);
}
}
Now, for every table you work with, you maintain an instance of this class. It allows you to Get/Set field values, keeping the indices cached.