diff --git a/ClosedXML/Excel/Coordinates/XLAddress.cs b/ClosedXML/Excel/Coordinates/XLAddress.cs index d8f5dcd..9e55f78 100644 --- a/ClosedXML/Excel/Coordinates/XLAddress.cs +++ b/ClosedXML/Excel/Coordinates/XLAddress.cs @@ -4,7 +4,7 @@ namespace ClosedXML.Excel { - internal class XLAddress : IXLAddress + internal struct XLAddress : IXLAddress, IEquatable { #region Static @@ -18,11 +18,6 @@ return Create(null, cellAddressString); } - public static XLAddress Create(XLAddress cellAddress) - { - return new XLAddress(cellAddress.Worksheet, cellAddress.RowNumber, cellAddress.ColumnNumber, cellAddress.FixedRow, cellAddress.FixedColumn); - } - public static XLAddress Create(XLWorksheet worksheet, string cellAddressString) { var fixedColumn = cellAddressString[0] == '$'; @@ -85,9 +80,6 @@ private bool _fixedColumn; [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string _columnLetter; - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly int _rowNumber; [DebuggerBrowsable(DebuggerBrowsableState.Never)] @@ -100,7 +92,7 @@ #region Constructors /// - /// Initializes a new struct using a mixed notation. Attention: without worksheet for calculation only! + /// Initializes a new struct using a mixed notation. Attention: without worksheet for calculation only! /// /// The row number of the cell address. /// The column letter of the cell address. @@ -112,7 +104,7 @@ } /// - /// Initializes a new struct using a mixed notation. + /// Initializes a new struct using a mixed notation. /// /// /// The row number of the cell address. @@ -122,11 +114,10 @@ public XLAddress(XLWorksheet worksheet, int rowNumber, string columnLetter, bool fixedRow, bool fixedColumn) : this(worksheet, rowNumber, XLHelper.GetColumnNumberFromLetter(columnLetter), fixedRow, fixedColumn) { - _columnLetter = columnLetter; } /// - /// Initializes a new struct using R1C1 notation. Attention: without worksheet for calculation only! + /// Initializes a new struct using R1C1 notation. Attention: without worksheet for calculation only! /// /// The row number of the cell address. /// The column number of the cell address. @@ -138,21 +129,20 @@ } /// - /// Initializes a new struct using R1C1 notation. + /// Initializes a new struct using R1C1 notation. /// /// /// The row number of the cell address. /// The column number of the cell address. /// /// - public XLAddress(XLWorksheet worksheet, int rowNumber, int columnNumber, bool fixedRow, bool fixedColumn) + public XLAddress(XLWorksheet worksheet, int rowNumber, int columnNumber, bool fixedRow, bool fixedColumn) : this() { Worksheet = worksheet; _rowNumber = rowNumber; _columnNumber = columnNumber; - _columnLetter = null; _fixedColumn = fixedColumn; _fixedRow = fixedRow; } @@ -188,7 +178,7 @@ } /// - /// Gets the row number of this address. + /// Gets the row number of this address. /// public Int32 RowNumber { @@ -196,7 +186,7 @@ } /// - /// Gets the column number of this address. + /// Gets the column number of this address. /// public Int32 ColumnNumber { @@ -204,11 +194,11 @@ } /// - /// Gets the column letter(s) of this address. + /// Gets the column letter(s) of this address. /// public String ColumnLetter { - get { return _columnLetter ?? (_columnLetter = XLHelper.GetColumnLetterFromNumber(_columnNumber)); } + get { return XLHelper.GetColumnLetterFromNumber(_columnNumber); } } #endregion Properties @@ -355,17 +345,21 @@ public bool Equals(IXLAddress other) { - var right = other as XLAddress; - if (ReferenceEquals(right, null)) - { + if (other == null) return false; - } - return _rowNumber == right._rowNumber && _columnNumber == right._columnNumber; + + return _rowNumber == other.RowNumber && + _columnNumber == other.ColumnNumber; + } + public bool Equals(XLAddress other) + { + return _rowNumber == other._rowNumber && + _columnNumber == other._columnNumber; } public override Boolean Equals(Object other) { - return Equals((XLAddress)other); + return Equals(other as IXLAddress); } #endregion IEquatable Members diff --git a/ClosedXML/Excel/Ranges/XLRangeAddress.cs b/ClosedXML/Excel/Ranges/XLRangeAddress.cs index 2594558..f1afeb4 100644 --- a/ClosedXML/Excel/Ranges/XLRangeAddress.cs +++ b/ClosedXML/Excel/Ranges/XLRangeAddress.cs @@ -26,8 +26,8 @@ public XLRangeAddress(XLAddress firstAddress, XLAddress lastAddress) { Worksheet = firstAddress.Worksheet; - FirstAddress = XLAddress.Create(firstAddress); - LastAddress = XLAddress.Create(lastAddress); + FirstAddress = firstAddress; + LastAddress = lastAddress; } public XLRangeAddress(XLWorksheet worksheet, String rangeAddress) @@ -113,14 +113,26 @@ { [DebuggerStepThrough] get { return FirstAddress; } - set { FirstAddress = value as XLAddress; } + set + { + if (value is XLAddress) + FirstAddress = (XLAddress)value; + else + FirstAddress = new XLAddress(value.RowNumber, value.ColumnNumber, value.FixedRow, value.FixedColumn); + } } IXLAddress IXLRangeAddress.LastAddress { [DebuggerStepThrough] get { return LastAddress; } - set { LastAddress = value as XLAddress; } + set + { + if (value is XLAddress) + LastAddress = (XLAddress)value; + else + LastAddress = new XLAddress(value.RowNumber, value.ColumnNumber, value.FixedRow, value.FixedColumn); + } } public bool IsValid { get; set; } = true; diff --git a/ClosedXML/Excel/Ranges/XLRangeBase.cs b/ClosedXML/Excel/Ranges/XLRangeBase.cs index f2a9831..0e78b5a 100644 --- a/ClosedXML/Excel/Ranges/XLRangeBase.cs +++ b/ClosedXML/Excel/Ranges/XLRangeBase.cs @@ -863,8 +863,8 @@ public XLRange Range(IXLCell firstCell, IXLCell lastCell) { - var newFirstCellAddress = firstCell.Address as XLAddress; - var newLastCellAddress = lastCell.Address as XLAddress; + var newFirstCellAddress = (XLAddress)firstCell.Address; + var newLastCellAddress = (XLAddress)lastCell.Address; return GetRange(newFirstCellAddress, newLastCellAddress); } @@ -908,7 +908,7 @@ public XLRange Range(IXLAddress firstCellAddress, IXLAddress lastCellAddress) { - var rangeAddress = new XLRangeAddress(firstCellAddress as XLAddress, lastCellAddress as XLAddress); + var rangeAddress = new XLRangeAddress((XLAddress)firstCellAddress, (XLAddress)lastCellAddress); return Range(rangeAddress); } @@ -1428,7 +1428,7 @@ public Boolean Contains(IXLCell cell) { - return Contains(cell.Address as XLAddress); + return Contains((XLAddress)cell.Address); } public bool Contains(XLAddress first, XLAddress last) diff --git a/ClosedXML/Excel/XLWorksheet.cs b/ClosedXML/Excel/XLWorksheet.cs index b29631d..d5cc301 100644 --- a/ClosedXML/Excel/XLWorksheet.cs +++ b/ClosedXML/Excel/XLWorksheet.cs @@ -49,8 +49,10 @@ RangeShiftedColumns = new XLReentrantEnumerableSet(); RangeAddress.Worksheet = this; - RangeAddress.FirstAddress.Worksheet = this; - RangeAddress.LastAddress.Worksheet = this; + RangeAddress.FirstAddress = new XLAddress(this, RangeAddress.FirstAddress.RowNumber, RangeAddress.FirstAddress.ColumnNumber, + RangeAddress.FirstAddress.FixedRow, RangeAddress.FirstAddress.FixedColumn); + RangeAddress.LastAddress = new XLAddress(this, RangeAddress.LastAddress.RowNumber, RangeAddress.LastAddress.ColumnNumber, + RangeAddress.LastAddress.FixedRow, RangeAddress.LastAddress.FixedColumn); Pictures = new XLPictures(this); NamedRanges = new XLNamedRanges(this); diff --git a/ClosedXML/XLHelper.cs b/ClosedXML/XLHelper.cs index e505099..ea324cd 100644 --- a/ClosedXML/XLHelper.cs +++ b/ClosedXML/XLHelper.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; @@ -53,6 +54,29 @@ RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture ); + private static readonly string[] letters = new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; + + private static readonly string[] allLetters; + private static readonly Dictionary letterIndexes; + + static XLHelper() + { + allLetters = new string[XLHelper.MaxColumnNumber]; + letterIndexes = new Dictionary(XLHelper.MaxColumnNumber, StringComparer.Create(ParseCulture, true)); + for (int i = 0; i < XLHelper.MaxColumnNumber; i++) + { + string letter; + if (i < 26) + letter = letters[i]; + else if (i < 26 * 27) + letter = letters[i / 26 - 1] + letters[i % 26]; + else + letter = letters[i / 26 / 26 - 1] + letters[(i / 26 - 1) % 26] + letters[i % 26]; + allLetters[i] = letter; + letterIndexes.Add(letter, i + 1); + } + } + /// /// Gets the column number of a given column letter. /// @@ -71,18 +95,12 @@ return retVal; } - int sum = 0; + if (letterIndexes.TryGetValue(columnLetter, out retVal)) + return retVal; - for (int i = 0; i < columnLetter.Length; i++) - { - sum *= 26; - sum += (columnLetter[i] - 'A' + 1); - } - - return sum; + throw new ArgumentOutOfRangeException(columnLetter + " is not recognized as a column letter"); } - private static readonly string[] letters = new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; /// /// Gets the column letter of a given column number. @@ -93,15 +111,8 @@ public static string GetColumnLetterFromNumber(int columnNumber, bool trimToAllowed = false) { if (trimToAllowed) columnNumber = TrimColumnNumber(columnNumber); - - columnNumber--; // Adjust for start on column 1 - if (columnNumber <= 25) - { - return letters[columnNumber]; - } - var firstPart = (columnNumber) / 26; - var remainder = ((columnNumber) % 26) + 1; - return GetColumnLetterFromNumber(firstPart) + GetColumnLetterFromNumber(remainder); + // Adjust for start on column 1 + return allLetters[columnNumber - 1]; } internal static int TrimColumnNumber(int columnNumber)