Skip to content

Commit

Permalink
Add methods to get implicit or explicit conversion operators from a type
Browse files Browse the repository at this point in the history
  • Loading branch information
skrysmanski committed Jul 7, 2024
1 parent 4ee0abf commit 6ed9e95
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 0 deletions.
88 changes: 88 additions & 0 deletions src/AppMotor.Core/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,4 +616,92 @@ static bool FindOpenGenericInterfaceFilter(Type interfaceTypeToCheck, object? cr
return type.GetMethod("op_" + @operator, BindingFlags.Public | BindingFlags.Static, [otherType, type]);
}
}

/// <summary>
/// Returns the implicit operator to convert from <paramref name="otherType"/> to this type. Returns <c>null</c>
/// if no such operator exists.
/// </summary>
/// <seealso cref="GetImplicitOperatorTo"/>
/// <seealso cref="GetExplicitOperatorFrom"/>
[MustUseReturnValue]
public static MethodInfo? GetImplicitOperatorFrom(this Type type, Type otherType)
{
return type.GetConversionOperatorFrom("op_Implicit", otherType);
}

/// <summary>
/// Returns the implicit operator to convert from this type to <paramref name="otherType"/>. Returns <c>null</c>
/// if no such operator exists.
/// </summary>
/// <seealso cref="GetImplicitOperatorFrom"/>
/// <seealso cref="GetExplicitOperatorTo"/>
[MustUseReturnValue]
public static MethodInfo? GetImplicitOperatorTo(this Type type, Type otherType)
{
return type.GetConversionOperatorTo("op_Implicit", otherType);
}

/// <summary>
/// Returns the explicit operator to convert from <paramref name="otherType"/> to this type. Returns <c>null</c>
/// if no such operator exists.
/// </summary>
/// <seealso cref="GetExplicitOperatorTo"/>
/// <seealso cref="GetImplicitOperatorFrom"/>
[MustUseReturnValue]
public static MethodInfo? GetExplicitOperatorFrom(this Type type, Type otherType)
{
return type.GetConversionOperatorFrom("op_Explicit", otherType);
}

/// <summary>
/// Returns the explicit operator to convert from this type to <paramref name="otherType"/>. Returns <c>null</c>
/// if no such operator exists.
/// </summary>
/// <seealso cref="GetExplicitOperatorFrom"/>
/// <seealso cref="GetImplicitOperatorTo"/>
[MustUseReturnValue]
public static MethodInfo? GetExplicitOperatorTo(this Type type, Type otherType)
{
return type.GetConversionOperatorTo("op_Explicit", otherType);
}

[MustUseReturnValue]
private static MethodInfo? GetConversionOperatorFrom(this Type type, string operatorName, Type otherType)
{
return type.GetMethod(operatorName, BindingFlags.Public | BindingFlags.Static, [otherType]);
}

[MustUseReturnValue]
private static MethodInfo? GetConversionOperatorTo(this Type type, string operatorName, Type otherType)
{
// NOTE: In this case, the operator methods only differ by return type - for which there
// is no support in type.GetMethod(). Thus, we have to iterate all methods.
foreach (var methodInfo in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
{
if (methodInfo.Name != operatorName)
{
continue;
}

var parameters = methodInfo.GetParameters();
if (parameters.Length != 1)
{
// I guess we could get here if someone created manually a static method named "op_Implicit".
continue;
}

if (parameters[0].ParameterType != type)
{
// Doesn't convert from this type.
continue;
}

if (methodInfo.ReturnType == otherType)
{
return methodInfo;
}
}

return null;
}
}
68 changes: 68 additions & 0 deletions tests/AppMotor.Core.Tests/Tests/Extensions/TypeExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,58 @@ public void Test_GetOperator_InvalidOperator()
Should.Throw<ArgumentException>(() => typeof(OperatorsTestContainer).GetOperator("xxx", typeof(string)));
}

[Fact]
public void Test_GetImplicitOperatorFrom()
{
// Test
var operatorInfo = typeof(OperatorsTestContainer).GetImplicitOperatorFrom(typeof(int));

// Verify
operatorInfo.ShouldNotBeNull();
operatorInfo.ReturnType.ShouldBe(typeof(OperatorsTestContainer));
operatorInfo.GetParameters().Select(p => p.ParameterType).ShouldBe([typeof(int)]);
}

[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(bool))]
public void Test_GetImplicitOperatorTo(Type otherType)
{
// Test
var operatorInfo = typeof(OperatorsTestContainer).GetImplicitOperatorTo(otherType);

// Verify
operatorInfo.ShouldNotBeNull();
operatorInfo.ReturnType.ShouldBe(otherType);
operatorInfo.GetParameters().Select(p => p.ParameterType).ShouldBe([typeof(OperatorsTestContainer)]);
}

[Fact]
public void Test_GetExplicitOperatorFrom()
{
// Test
var operatorInfo = typeof(OperatorsTestContainer).GetExplicitOperatorFrom(typeof(long));

// Verify
operatorInfo.ShouldNotBeNull();
operatorInfo.ReturnType.ShouldBe(typeof(OperatorsTestContainer));
operatorInfo.GetParameters().Select(p => p.ParameterType).ShouldBe([typeof(long)]);
}

[Theory]
[InlineData(typeof(ulong))]
[InlineData(typeof(ushort))]
public void Test_GetExplicitOperatorTo(Type otherType)
{
// Test
var operatorInfo = typeof(OperatorsTestContainer).GetExplicitOperatorTo(otherType);

// Verify
operatorInfo.ShouldNotBeNull();
operatorInfo.ReturnType.ShouldBe(otherType);
operatorInfo.GetParameters().Select(p => p.ParameterType).ShouldBe([typeof(OperatorsTestContainer)]);
}

#pragma warning disable CS0660, CS0661
private sealed class OperatorsTestContainer
#pragma warning restore CS0660, CS0661
Expand Down Expand Up @@ -820,6 +872,22 @@ private sealed class OperatorsTestContainer

#endregion Binary Operators

#region Implicit & Explicit Operators

public static implicit operator OperatorsTestContainer(int value) => throw new NotSupportedException();

// NOTE: The following two operators just differ by their return type - which is normally not supported for C# methods.
public static implicit operator string(OperatorsTestContainer value) => throw new NotSupportedException();
public static implicit operator bool(OperatorsTestContainer value) => throw new NotSupportedException();

public static explicit operator OperatorsTestContainer(long value) => throw new NotSupportedException();

// NOTE: The following two operators just differ by their return type - which is normally not supported for C# methods.
public static explicit operator ulong(OperatorsTestContainer value) => throw new NotSupportedException();
public static explicit operator ushort(OperatorsTestContainer value) => throw new NotSupportedException();

#endregion Implicit & Explicit Operators

// ReSharper restore UnusedParameter.Local
}
}

0 comments on commit 6ed9e95

Please sign in to comment.