I've designed a little struct that should help me deal with Excel columns in terms when the letter index is used. The idea is to use the struct implicitly most of the time for iterations over Excel columns etc.
The struct works in the cases i've intended it to. However, on the one hand, I'm not sure if it isn't a little over the top for a struct and i should rather use a class. On the other hand I was a little surprised why the struct did actually work implicitly when combining it with an integer and assigning it to a string value like this
int rowIndex = 1;
ExcelColumn column = 1;
string cellId = column + rowIndex.ToString();
results in the string
being assigned to
A1
So my questions are:
- Why is that the case?
- Is the struct properly designed for its purpose?
Below is the code for the struct:
public struct ExcelColumn
{
#region Constants
private const int MAX_COLUMN_COUNT = 16384;
#endregion
#region Fields
private readonly int _value;
#endregion
#region Constructors
public ExcelColumn(int columnIndex)
{
if (columnIndex < 1)
throw new IndexOutOfRangeException("Index below 1 not allowed.");
if (columnIndex > MAX_COLUMN_COUNT)
throw new OverflowException("Max Column number exceeded");
_value = columnIndex;
}
#endregion
#region Methods
private string GetValue()
{
string value = "";
int val = _value - 1;
do
{
value = Convert.ToChar(65 + val % 26) + value;
val = (val - val % 26) / 26 - 1;
}
while (val >= 0);
return value;
}
public override string ToString()
{
return GetValue();
}
#endregion
#region Operators
public static ExcelColumn operator +(ExcelColumn x, ExcelColumn y)
{
return new ExcelColumn(x._value + y._value);
}
public static ExcelColumn operator +(ExcelColumn x, int y)
{
return new ExcelColumn(x._value + y);
}
public static ExcelColumn operator +(int x, ExcelColumn y)
{
return new ExcelColumn(x + y._value);
}
public static ExcelColumn operator ++(ExcelColumn x)
{
return new ExcelColumn(x._value + 1);
}
public static ExcelColumn operator -(ExcelColumn x, ExcelColumn y)
{
return new ExcelColumn(x._value - y._value);
}
public static ExcelColumn operator -(ExcelColumn x, int y)
{
return new ExcelColumn(x._value - y);
}
public static ExcelColumn operator -(int x, ExcelColumn y)
{
return new ExcelColumn(x - y._value);
}
public static ExcelColumn operator --(ExcelColumn x)
{
return new ExcelColumn(x._value - 1);
}
public static implicit operator ExcelColumn(int x)
{
return new ExcelColumn(x);
}
public static explicit operator int(ExcelColumn x)
{
return x._value;
}
#endregion
#region Properties
public string Value { get { return GetValue(); } }
public int Index { get { return _value; } }
#endregion
}
1 Answer 1
Why is that the case?
You need to know what the string +
operator does under the covers - it calls String.Concat
.
So when you do this:
var someString = aString + anObject;
We get this:
var someString = string.Concat(aString, anObject);
Let's look at the IL for your example:
C#
void Main()
{
int rowIndex = 1;
ExcelColumn column = new ExcelColumn(1);
string cellId = column + rowIndex.ToString();
}
IL
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0 // rowIndex
IL_0003: ldloca.s 01 // column
IL_0005: ldc.i4.1
IL_0006: call UserQuery+ExcelColumn..ctor
IL_000B: ldloc.1 // column
IL_000C: box UserQuery.ExcelColumn
IL_0011: ldloca.s 00 // rowIndex
IL_0013: call System.Int32.ToString
IL_0018: call System.String.Concat
IL_001D: stloc.2 // cellId
IL_001E: ret
You can see that your ExcelColumn
(column) is being boxed to an object
and passed to String.Concat
.
So your question is really: why does this work?
string cellId = string.Concat(column, rowIndex.ToString());
Answer: because column.ToString()
is being called in string.Concat
.
Hmmm... I hope that makes sense.
Time to review your code.
Get rid of the regions. They're almost always pointless and you certainly don't need a region around a single constant.
Constants are named PascalCase
in C#. MAX_COLUMN_COUNT
should be MaxColumnCount
.
I think it would be better to store the string representation at creation rather than computing it each time it's needed.
Structs always have a default constructor which means you have a bug.
new ExcelColumn().ToString() // @
-
\$\begingroup\$ But if I do store the string representation, wouldn't default constructor just initialize that string as
null
which would be equally false? \$\endgroup\$Adwaenyth– Adwaenyth2017年04月05日 09:35:26 +00:00Commented Apr 5, 2017 at 9:35 -
\$\begingroup\$ @Adwaenyth - the string comment is separate to the default constructor comment. I'd recommend making this a class if you want your indexes to be 1-based rather than 0 based. \$\endgroup\$RobH– RobH2017年04月05日 09:38:41 +00:00Commented Apr 5, 2017 at 9:38
int
norExcelColumn
define an implicit conversion to string, I'm finding it hard to believe that this compiles:string cellId = column + rowIndex;
. \$\endgroup\$.ToString();
call. Edited it in the question. \$\endgroup\$