Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 3c0c728

Browse files
committed
Add support for using implicit/explicit casts of Value types in ConvertTo
1 parent 592d63b commit 3c0c728

File tree

2 files changed

+87
-5
lines changed

2 files changed

+87
-5
lines changed

src/ServiceStack.Text/AutoMappingUtils.cs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,10 @@ public static T ConvertTo<T>(this object from, T defaultValue) =>
8787
public static T ConvertTo<T>(this object from, bool skipConverters)
8888
{
8989
if (from == null)
90-
return default(T);
90+
return default;
9191

92-
var fromType = from.GetType();
93-
if (fromType == typeof(T))
94-
return (T)from;
92+
if (from is T t)
93+
return t;
9594

9695
return (T)ConvertTo(from, typeof(T), skipConverters);
9796
}
@@ -161,6 +160,32 @@ public static object ConvertTo(this object from, Type toType, bool skipConverter
161160
return to.PopulateWithNonDefaultValues(from);
162161
}
163162

163+
public static MethodInfo GetImplicitCastMethod(Type fromType, Type toType)
164+
{
165+
foreach (var mi in fromType.GetMethods(BindingFlags.Public | BindingFlags.Static))
166+
{
167+
if (mi.Name == "op_Implicit" && mi.ReturnType == toType &&
168+
mi.GetParameters().FirstOrDefault()?.ParameterType == fromType)
169+
{
170+
return mi;
171+
}
172+
}
173+
return null;
174+
}
175+
176+
public static MethodInfo GetExplicitCastMethod(Type fromType, Type toType)
177+
{
178+
foreach (var mi in toType.GetMethods(BindingFlags.Public | BindingFlags.Static))
179+
{
180+
if (mi.Name == "op_Explicit" && mi.ReturnType == toType &&
181+
mi.GetParameters().FirstOrDefault()?.ParameterType == fromType)
182+
{
183+
return mi;
184+
}
185+
}
186+
return null;
187+
}
188+
164189
public static object ChangeValueType(object from, Type toType)
165190
{
166191
var s = from as string;
@@ -200,7 +225,15 @@ public static object ChangeValueType(object from, Type toType)
200225
return srcNumberType.ToString(from);
201226
}
202227
}
203-
228+
229+
var mi = GetImplicitCastMethod(fromType, toType);
230+
if (mi != null)
231+
return mi.Invoke(null, new[] { from });
232+
233+
mi = GetExplicitCastMethod(fromType, toType);
234+
if (mi != null)
235+
return mi.Invoke(null, new[] { from });
236+
204237
if (s != null)
205238
return TypeSerializer.DeserializeFromString(s, toType);
206239

tests/ServiceStack.Text.Tests/AutoMappingTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,5 +1098,54 @@ public void To_ValueType_on_null_or_empty_string_returns_default_value()
10981098
Assert.That("".ConvertTo<int>(), Is.EqualTo(default(int)));
10991099
}
11001100

1101+
struct A
1102+
{
1103+
public int Id { get; }
1104+
public A(int id) => Id = id;
1105+
public static implicit operator B(A from) => new B(from.Id);
1106+
}
1107+
1108+
struct B
1109+
{
1110+
public int Id { get; }
1111+
public B(int id) => Id = id;
1112+
public static implicit operator A(B from) => new A(from.Id);
1113+
}
1114+
1115+
[Test]
1116+
public void Can_convert_between_structs_with_implicit_casts()
1117+
{
1118+
var a = new A(1);
1119+
var b = a;
1120+
Assert.That(b.Id, Is.EqualTo(a.Id));
1121+
1122+
b = a.ConvertTo<B>();
1123+
Assert.That(b.Id, Is.EqualTo(a.Id));
1124+
}
1125+
1126+
struct C
1127+
{
1128+
public int Id { get; }
1129+
public C(int id) => Id = id;
1130+
public static explicit operator C(D from) => new C(from.Id);
1131+
}
1132+
1133+
struct D
1134+
{
1135+
public int Id { get; }
1136+
public D(int id) => Id = id;
1137+
public static explicit operator D(C from) => new D(from.Id);
1138+
}
1139+
1140+
[Test]
1141+
public void Can_convert_between_structs_with_explicit_casts()
1142+
{
1143+
var c = new C(1);
1144+
var d = (D)c;
1145+
Assert.That(d.Id, Is.EqualTo(c.Id));
1146+
1147+
d = c.ConvertTo<D>();
1148+
Assert.That(d.Id, Is.EqualTo(c.Id));
1149+
}
11011150
}
11021151
}

0 commit comments

Comments
 (0)