Ok, after a brief hiatus over the summer where time to blog just didn't seem to exist, I figure it's time to get back to it.
So, for the last week or so I've been converting some custom controls that I originally wrote in VB.NET into C#, since our client has decided to switch their standard. This isn't terribly difficult, mostly just tedious, which makes it all the more frustrating when the 3 lines of code that were working in the VB version don't work in the C#, for no apparent reason. Once I finally figured it out, all I can say is that it would have been a lot easier if the debugger would have broke on the line that actually threw the error, instead of on the next line...
So, here is the VB code. This code is trying to find the name of an object used to track transactions in a DAL generated using LLBL Gen Pro, but the actual name of the DAL is not known at compile time, so it uses reflection to find the correct assembly and load the transaction. The transaction object does not have a default constructor, the simplist constructor takes 2 parameters.
'Get a transaction object from the DAL
'Get the assembly that is loaded that contains the type of the DataSource (in the DAL).
Dim a As System.Reflection.Assembly = System.Reflection.Assembly.GetAssembly(CType(DataSource, Object).GetType)
'Get the root namespace from the assembly
Dim RootNamespace = a.FullName.Substring(0, a.FullName.IndexOf(","))
'Convert the entity name into the complete name of a transaction object
Dim FullName As String = RootNamespace & ".HelperClasses.Transaction"
'Setup the params for the constructor
Dim params(1) As Object
params(0) = System.Data.IsolationLevel.RepeatableRead
params(1) = "del" 'Name of transaction
'Get the transaction object
Dim tran As ORMSupportClasses.ITransaction = a.CreateInstance(FullName, True, Reflection.BindingFlags.CreateInstance, Nothing, params, _ System.Globalization.CultureInfo.CurrentCulture, Nothing)
And here is the same code in C#. When this is run, it throws an error or "Index out of bounds" and stops on the line that uses the assembly to create the transaction.
//Get a transaction object from the DAL
//Get the assembly that is loaded that contains the type of the
//DataSource (in the DAL).
System.Reflection.Assembly a = System.Reflection.Assembly.GetAssembly(((object)DataSource).GetType());
//Get the root namespace from the assembly
string RootNamespace = a.FullName.Substring(0, a.FullName.IndexOf(","));
//Convert the entity name into the complete name of a transaction object
string FullName = RootNamespace + ".HelperClasses.Transaction";
//Setup the params for the constructor.
object[] p = new object[1];
p[0] = System.Data.IsolationLevel.RepeatableRead;
p[1] = "del";
//Get the transaction object from the DAL assembly
SD.LLBLGen.Pro.ORMSupportClasses.ITransaction tran = (SD.LLBLGen.Pro.ORMSupportClasses.ITransaction)a.CreateInstance(FullName, true, System.Reflection.BindingFlags.CreateInstance, null, p, System.Globalization.CultureInfo.CurrentCulture, null);
Anone spot the problem? I spent hours trying to figure out why the constructor was failing. The truth is, it's not the constructor at all. The compiler is stopping on the wrong line! It should have stopped on the next line up, where p[1] is assigned.
You see, when you define an array in VB.NET, the number that you put in the brackets is the maximum index that is allowed in the array, which makes every VB.NET array one element larger then the number specified (since arrays are zero based), so it is 2 elements big in this case. However, in C# the number you enter in the definition is the actual element count that the array should have, so in this example, I only get one element, which is p[0]. I changed the array declaration to allow for 2 elements and presto, everything works.
Now this would have been way easier to find if Visual Studio hadn't lead me astray. Instead of spending hours trying to figure out why the constructor was working in VB and not in C#, which was a reasonable assumption since the constructor was consuming the array and there are other constructors which require more parameters, I could have immediately figured out that I'd simply declared my array too small if it would have stopped on the line where p[1] was actually set.
So, what I learned today is...
- If you ever get an Index Out Of Range error, check your array declarations first, regardless of which line Visual Studio reports the error from. This is especially true if you accessed an array on the previous line of code from where Visual Studio stops.