Skip to content

Instantly share code, notes, and snippets.

@msedi
Last active June 10, 2020 10:52
Show Gist options
  • Save msedi/04c1e26366e58ac7ceb9e01b18f70dde to your computer and use it in GitHub Desktop.
Save msedi/04c1e26366e58ac7ceb9e01b18f70dde to your computer and use it in GitHub Desktop.
Equals vs. == in struct comparison
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
namespace ConsoleApp21
{
class Program
{
static void Main(string[] args)
{
RangeDescriptorGood good = new RangeDescriptorGood();
Console.WriteLine("Comparison with ==");
Console.WriteLine($"Good == default: {good == default}");
Console.WriteLine($"Good == null: {good == null}");
Console.WriteLine();
Console.WriteLine("Comparison with Equals");
Console.WriteLine($"Good.Equals(default): {good.Equals(default)}");
Console.WriteLine($"Good.Equals(null): {good.Equals(null)}");
}
/// <summary>
/// Class that is able to resolve a range expression into an array of indices.
/// </summary>
public readonly struct RangeDescriptorGood : IEquatable<RangeDescriptorGood>
{
private readonly bool IsInstance;
/// <summary>
/// List of Indices.
/// </summary>
public readonly string RangeString;
/// <summary>
/// ctor.
/// </summary>
/// <param name="indices"></param>
private RangeDescriptorGood(string rangeString)
{
IsInstance = true;
RangeString = rangeString;
}
/// <summary>
///
/// </summary>
/// <param name="v1"></param>
/// <param name="v2"></param>
public RangeDescriptorGood(int v1, int v2)
{
IsInstance = true;
RangeString = $"{v1}-{v2}";
}
/// <summary>
/// Parses a range expression, given in a-b, a-, -b, or in a comma separated list or both.
/// </summary>
/// <param name="rangeString_in"></param>
/// <param name="minVal"></param>
/// <param name="maxVal"></param>
/// <returns></returns>
public static RangeDescriptorGood Parse(string rangeString_in)
{
if (string.IsNullOrEmpty(rangeString_in)) throw new ArgumentNullException(nameof(rangeString_in));
return new RangeDescriptorGood(rangeString_in);
}
public int[] GetIndices(int minVal, int maxVal)
{
string[] items = RangeString.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
List<int> Indices = new List<int>(Math.Max(maxVal - minVal, 1));
foreach (string item in items)
{
if (!string.IsNullOrEmpty(item))
{
if (item.Contains('-'))
{
string[] it = item.Split('-');
string v1 = it[0].Trim();
string v2 = it[1].Trim();
int i1 = string.IsNullOrEmpty(v1) ? minVal : int.Parse(v1, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
int i2 = string.IsNullOrEmpty(v2) ? minVal : int.Parse(v2, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
// Swap if necessary.
if (i1 > i2)
{
int tmp = i1;
i1 = i2;
i2 = tmp;
}
for (int idx = i1; idx <= i2; idx++)
{
Indices.Add(idx);
}
}
else
{
int idx = int.Parse(item, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
Indices.Add(idx);
}
}
}
// Sort the list, remove same values, shrink to valid range
#warning This has to be evaluated since it was meant that the range goes from min to max (including both) not excluding the end
return (from x in Indices orderby x ascending where (x >= minVal) && (x < maxVal) select x).Distinct().ToArray();
}
/// <summary>
/// Evaluates the range and returns the indices that shall be evaluated.
/// </summary>
/// <param name="maxRangeItemsCount_in"></param>
/// <param name="range_in"></param>
/// <returns></returns>
public static IReadOnlyList<int> GetRangeIndex(int maxRangeItemsCount_in, RangeDescriptorGood range_in = default)
{
List<int> aIndexes = new List<int>();
// Get the necessary range
// If no range is given, we take the full range.
if (range_in == default)
{
aIndexes.AddRange(Enumerable.Range(0, maxRangeItemsCount_in));
}
// If a range is given, we need to interpret the values.
else
{
aIndexes.AddRange(range_in.GetIndices(0, maxRangeItemsCount_in));
}
return aIndexes;
}
/// <summary>
/// Maps the given range to a new range starting with a 0 target index.
/// </summary>
/// <param name="maxRangeItemsCount"></param>
/// <param name="range_in"></param>
/// <returns></returns>
public static IReadOnlyList<RangeItem> MapRange(int maxRangeItemsCount, RangeDescriptorGood range_in = default)
{
List<RangeItem> aSourceToTargetIndices = new List<RangeItem>();
var aIndexes = GetRangeIndex(maxRangeItemsCount, range_in);
for (int aTargetIndex = 0; aTargetIndex < aIndexes.Count; aTargetIndex++)
{
aSourceToTargetIndices.Add(new RangeItem(aIndexes[aTargetIndex], aTargetIndex));
}
return aSourceToTargetIndices;
}
/// <summary>
/// Checks for equality.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
// If null is handed over, we assume that default was meant.
obj ??= default(RangeDescriptorGood);
if (!(obj is RangeDescriptorGood value))
return false;
return Equals(value);
}
/// <summary>
/// Checks for equality.
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public bool Equals([AllowNull] RangeDescriptorGood other)
{
if (!IsInstance && !other.IsInstance)
return true;
return string.Equals(RangeString, other.RangeString);
}
public override int GetHashCode() => RangeString.GetHashCode();
public static bool operator ==(RangeDescriptorGood left, RangeDescriptorGood right) => Equals(left, right);
public static bool operator !=(RangeDescriptorGood left, RangeDescriptorGood right) => !Equals(left, right);
}
public readonly struct RangeItem
{
public readonly int TargetIndex;
public readonly int SourceIndex;
public RangeItem(int sourceIndex_in, int targetIndex_in)
{
TargetIndex = targetIndex_in;
SourceIndex = sourceIndex_in;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment