C# 10에 추가될 static abstract와 함께 제네릭 Math가 도입될 예정입니다. 최신 Visual Studio 2022 환경에서 이 기능을 미리보기 할 수 있습니다.
이제, 숫자형은 점진적으로 INumber<TSelf>
로 취급되게 되므로 제네릭 Math가 보편적으로 사용되게 되면 완전한 사용자 유형의 숫자형을 만들 수 있게 됩니다.
아래 코드는 미구현된 코드임
record struct Point<TValue>(TValue x, TValue y) : INumber<Point<TValue>> where TValue : INumber<TValue>
{
public static Point<TValue> One => throw new NotImplementedException();
public static Point<TValue> Zero => throw new NotImplementedException();
public static Point<TValue> AdditiveIdentity => throw new NotImplementedException();
public static Point<TValue> MultiplicativeIdentity => throw new NotImplementedException();
public static Point<TValue> Abs(Point<TValue> value)
{
throw new NotImplementedException();
}
public static Point<TValue> Clamp(Point<TValue> value, Point<TValue> min, Point<TValue> max)
{
throw new NotImplementedException();
}
public static Point<TValue> Create<TOther>(TOther value) where TOther : INumber<TOther>
{
throw new NotImplementedException();
}
public static Point<TValue> CreateSaturating<TOther>(TOther value) where TOther : INumber<TOther>
{
throw new NotImplementedException();
}
public static Point<TValue> CreateTruncating<TOther>(TOther value) where TOther : INumber<TOther>
{
throw new NotImplementedException();
}
public static (Point<TValue> Quotient, Point<TValue> Remainder) DivRem(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
public static Point<TValue> Max(Point<TValue> x, Point<TValue> y)
{
throw new NotImplementedException();
}
public static Point<TValue> Min(Point<TValue> x, Point<TValue> y)
{
throw new NotImplementedException();
}
public static Point<TValue> Parse(string s, NumberStyles style, IFormatProvider? provider)
{
throw new NotImplementedException();
}
public static Point<TValue> Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider)
{
throw new NotImplementedException();
}
public static Point<TValue> Parse(ReadOnlySpan<char> s, IFormatProvider? provider)
{
throw new NotImplementedException();
}
public static Point<TValue> Parse(string s, IFormatProvider? provider)
{
throw new NotImplementedException();
}
public static Point<TValue> Sign(Point<TValue> value)
{
throw new NotImplementedException();
}
public static bool TryCreate<TOther>(TOther value, out Point<TValue> result) where TOther : INumber<TOther>
{
throw new NotImplementedException();
}
public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Point<TValue> result)
{
throw new NotImplementedException();
}
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out Point<TValue> result)
{
throw new NotImplementedException();
}
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out Point<TValue> result)
{
throw new NotImplementedException();
}
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Point<TValue> result)
{
throw new NotImplementedException();
}
public int CompareTo(object? obj)
{
throw new NotImplementedException();
}
public int CompareTo(Point<TValue> other)
{
throw new NotImplementedException();
}
public string ToString(string? format, IFormatProvider? formatProvider)
{
throw new NotImplementedException();
}
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
{
throw new NotImplementedException();
}
public static Point<TValue> operator +(Point<TValue> value)
{
throw new NotImplementedException();
}
public static Point<TValue> operator +(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
public static Point<TValue> operator -(Point<TValue> value)
{
throw new NotImplementedException();
}
public static Point<TValue> operator -(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
public static Point<TValue> operator ++(Point<TValue> value)
{
throw new NotImplementedException();
}
public static Point<TValue> operator --(Point<TValue> value)
{
throw new NotImplementedException();
}
public static Point<TValue> operator *(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
public static Point<TValue> operator /(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
public static Point<TValue> operator %(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
public static bool operator <(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
public static bool operator >(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
public static bool operator <=(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
public static bool operator >=(Point<TValue> left, Point<TValue> right)
{
throw new NotImplementedException();
}
}
오 굉장히 놀랍고 흥미롭습니다. 비단 INumber<TSelf>
을 사용하지 않더라도 static abstract
를 이용해 적절히 인터페이스를 만들고, 이를 대표 키 속성으로 만들 수도 있습니다. 최종적으로 이런 기법이 EF Core에도 적용되리라 생각이 드네요. (Validation 등 사용자 유형에 따라 일괄 처리하게 되어서 좀 더 깔끔한 코드가 예상됩니다.)