Skip to content

Commit f59e4f3

Browse files
committed
refactor(Utils): Refactor the Union class to improve type safety and state management
- Introduce the `UnionValueState` enum to explicitly track the currently stored value type - Refactor the constructor for safer initialization logic and add type compatibility checks - Override the `Equals`, `GetHashCode`, and `ToString` methods to comply with object conventions - Rename generic parameters from `TA`/`TB` to `T1`/`T2` for better consistency - Fix the logic for implicit conversion and value access to avoid null reference exceptions
1 parent 1e22ad6 commit f59e4f3

File tree

1 file changed

+79
-29
lines changed

1 file changed

+79
-29
lines changed

src/Utils/Union.cs

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,66 +6,116 @@
66

77
namespace Limekuma.Utils;
88

9+
internal enum UnionValueState
10+
{
11+
Empty,
12+
First,
13+
Second
14+
}
15+
916
[JsonConverter(typeof(UnionJsonConverter))]
10-
public class Union<TA, TB>
17+
public sealed class Union<T1, T2>
1118
{
12-
private readonly TA? _valueA;
13-
private readonly TB? _valueB;
19+
private readonly T1? _value1;
20+
private readonly T2? _value2;
21+
private readonly UnionValueState _valueState = UnionValueState.Empty;
1422

15-
public Union(TA? value) => _valueA = value;
16-
public Union(TB? value) => _valueB = value;
23+
public Union(T1? value) : this()
24+
{
25+
if (value is null)
26+
{
27+
return;
28+
}
29+
30+
_valueState = UnionValueState.First;
31+
_value1 = value;
32+
}
1733

18-
public Union(object? obj)
34+
public Union(T2? value) : this()
1935
{
20-
if (obj is null)
36+
if (value is null)
2137
{
2238
return;
2339
}
2440

25-
if (obj is TA valueA)
41+
_valueState = UnionValueState.Second;
42+
_value2 = value;
43+
}
44+
45+
public Union(object? value) : this()
46+
{
47+
switch (value)
48+
{
49+
case null:
50+
return;
51+
case T1 value1:
52+
_valueState = UnionValueState.First;
53+
_value1 = value1;
54+
break;
55+
case T2 value2:
56+
_valueState = UnionValueState.Second;
57+
_value2 = value2;
58+
break;
59+
default:
60+
throw new ArgumentException($"Union value must be of type {typeof(T1).Name} or {typeof(T2).Name}.", nameof(value));
61+
}
62+
}
63+
64+
private Union()
65+
{
66+
if (typeof(T1).IsAssignableFrom(typeof(T2)))
2667
{
27-
_valueA = valueA;
68+
throw new ArgumentException($"Union value type {typeof(T1).Name} is assignable from type {typeof(T2).Name}.");
2869
}
2970

30-
if (obj is TB valueB)
71+
if (typeof(T2).IsAssignableFrom(typeof(T1)))
3172
{
32-
_valueB = valueB;
73+
throw new ArgumentException($"Union value type {typeof(T2).Name} is assignable from type {typeof(T1).Name}.");
3374
}
3475
}
3576

3677
public object? Value
3778
{
3879
get
3980
{
40-
if (_valueA is not null)
81+
return _valueState switch
4182
{
42-
return _valueA;
43-
}
44-
45-
if (_valueB is not null)
46-
{
47-
return _valueB;
48-
}
49-
50-
return null;
83+
UnionValueState.Empty => null,
84+
UnionValueState.First => _value1,
85+
UnionValueState.Second => _value2,
86+
_ => throw new InvalidOperationException($"Union value type {_valueState} is not valid."),
87+
};
5188
}
5289
}
5390

54-
public static implicit operator TA?(Union<TA, TB> o) => o._valueA;
91+
public static implicit operator T1?(Union<T1, T2> o) => o._value1;
5592

56-
public static implicit operator TB?(Union<TA, TB> o) => o._valueB;
93+
public static implicit operator T2?(Union<T1, T2> o) => o._value2;
5794

58-
public static implicit operator Union<TA, TB>(TA a) => new(a);
95+
public static implicit operator Union<T1, T2>(T1 a) => new(a);
5996

60-
public static implicit operator Union<TA, TB>(TB b) => new(b);
97+
public static implicit operator Union<T1, T2>(T2 b) => new(b);
6198

62-
public new Type? GetType() => Value?.GetType();
99+
public Type? ValueType => Value?.GetType();
63100

64-
public new string? ToString() => Value?.ToString();
101+
public override string? ToString() => Value?.ToString();
65102

66-
public new bool? Equals(object? obj) => Value?.Equals(obj);
103+
public override bool Equals(object? obj)
104+
{
105+
if (obj is Union<T1, T2> value)
106+
{
107+
if (value._valueState != _valueState)
108+
{
109+
return false;
110+
}
111+
112+
return Value?.Equals(value.Value) ?? value.Value is null;
113+
}
114+
115+
return Value?.Equals(obj) ?? obj is null;
116+
}
67117

68-
public new int? GetHashCode() => Value?.GetHashCode();
118+
public override int GetHashCode() => Value?.GetHashCode() ?? 0;
69119
}
70120

71121
public sealed class UnionJsonConverter : JsonConverterFactory

0 commit comments

Comments
 (0)