4
\$\begingroup\$

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
}
asked Apr 5, 2017 at 5:42
\$\endgroup\$
4
  • 1
    \$\begingroup\$ Are you sure that's your test code? As neither int nor ExcelColumn define an implicit conversion to string, I'm finding it hard to believe that this compiles: string cellId = column + rowIndex; . \$\endgroup\$ Commented Apr 5, 2017 at 6:40
  • \$\begingroup\$ 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 - me either so I tested it and as expected and mentioned by @RobH it didn't work so how could it have worked for you? \$\endgroup\$ Commented Apr 5, 2017 at 6:47
  • \$\begingroup\$ @t3chb0t - glad I wasn't going mad - I was on my mac which doesn't have a C# compiler on it. I'm not convinced we should close the question because the struct (the main body of the question) is review-able... not sure whether we have a rule about this sort of thing. \$\endgroup\$ Commented Apr 5, 2017 at 7:32
  • \$\begingroup\$ @t3chb0t Whoops... copied the wrong code... was missing a .ToString(); call. Edited it in the question. \$\endgroup\$ Commented Apr 5, 2017 at 7:32

1 Answer 1

2
\$\begingroup\$

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() // @
answered Apr 5, 2017 at 8:41
\$\endgroup\$
2
  • \$\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\$ Commented 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\$ Commented Apr 5, 2017 at 9:38

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.