I need to define an Interface which has to enforce certain operator overloading to the types which implements it. There doesn't seem an obvious way to do it since operator overloading has to be done using static methods in class. Is there any way to achieve the same effect (using abstract classes or anything else)?
There's no new / delete expression in C. The closest equivalent are the malloc and free functions, if you ignore the constructors/destructors and type safety.
In C you do not have the this keyword. Only in C++ and in a class, so your code is C and you use your this variable as a local method parameter, where you access the array struct.
Bit of a hack, but...
You could provide operator overloads in your base class that then call some published abstract methods in one of the classes to do the job there.
public abstract class MyClass
{
public static MyClass operator +(MyClass c1, MyClass c2)
{
return c1.__DoAddition(c2);
}
protected abstract MyClass __DoAddition(MyClass c2);
}
No. The only sensible way to do this would be to have a unit test check use reflection to find all concrete implementations, and then verify this condition. You could also perhaps do something at runtime re the same via a static constructor, but then the question is which static constructor?
Another approach is to drop the operators and use an interface-based approach; for example , if you need T
to have +(T,T)
then instead of operators have an interface with an Add(T)
method. Another advantage here is that interfaces are usable from generics (typically via constraints), where-as using operators from generic code takes some effort.
You could implement the overloading in an abstract base class, but delegate the actually operation specifics to an abstract method. Then this will have to be implemented and the overloading will be don with their implementation.
public abstract class OverLoadingBase
{
public abstract OverLoadingBase DoAdd(OverLoadingBase y);
public static OverLoadingBase operator +(OverLoadingBase x, OverLoadingBase y)
{
return x.DoAdd(y);
}
}
Though I'm not sure if this is complete.
I have done this in the past...
public abstract class BaseClass<TClass> where TClass : BaseClass
{
public static TClass operator +(TClass c1, TClass c2)
{
return c1.DoAddition(c2);
}
protected abstract TClass DoAddition(TClass c2);
}
And then implemented as follows:
public class ConcreteClass : BaseClass<ConcreteClass>
{
protected ConcreteClass DoAddition(ConcreteClass c2)
{
...
}
}
Is there any way in C# to enforce operator overloading in derived classes?
Strictly speaking, types don't "derive" from an interface
, they only implement
it - but if you are referring to requiring operator overloads in a derived class
from a parent class
then that can be done by making the parent class
generic to allow for variant operator parameter and return types, but this effectively means that the subclass cannot then be subclassed again (without making the subclass generic).
Is there any way to achieve the same effect (using abstract classes or anything else)?
Now, assuming that you are referring to only interfaces, then yes, it's possible! The trick is to not have the operators defined on the interface nor it classes, but in a wrapper-struct
- which is used through the magic of operator implicit
...
There is a slight catch though, but which is entirely solvable provided you're using C# 6.0 or later... read on!
What you're describing is (another) good use-case for wrapper-struct
s over open generic interfaces.
class
) or value (struct
), including any interface
, is wrapped in a struct
invisibly to your program, and that wrapper-struct then adds the required functionality you're after.struct
etc) are "free" in .NET because they don't incur a GC heap allocation.To make this work, first define the interface with the operations you want to support.
public interface IOperable<TImpl,TValue> : IEquatable<TImpl>, IComparable<TImpl>
where TImpl : IOperable<TImpl,TValue>
where TValue : notnull
{
Operable<TImpl,TValue> Op { get; }
TImpl CreateFor( TValue other );
TImpl Self { get; }
TValue Value { get; }
TImpl Add( TValue other );
TImpl Subtract( TValue other );
TImpl Multiply( TValue other );
TImpl Divide( TValue other );
TImpl Remainder( TValue other );
TImpl Inverse();
}
struct Operable<TImpl,TValue>
).
static operator
methods which all accept and return the the same Operable<TImpl,TValue>
.struct Operable
also has implicit operator
defined for implicit conversion to-and-from TImpl
and to TValue
, which is what helps make this approach usable.TValue
isn't possible because there's no way of knowing what TImpl
would be, but you can define operators on struct Operable<>
that allow raw TValue
for either of the operands, that way it can infer TImpl
from the other operand.// Note that `struct Operable<T>` does not implement IOperable<T>.
public struct Operable<TImpl,TValue>
where TImpl : IOperable<TImpl,TValue>
where TValue : notnull
{
#region Operators (Self-to-Self)
public static Operable<TImpl,TValue> operator +(Operable<TImpl,TValue> lhs, Operable<TImpl,TValue> rhs)
{
TImpl result = lhs.Self.Add( rhs.Value );
return result;
}
public static Operable<TImpl,TValue> operator -(Operable<TImpl,TValue> lhs, Operable<TImpl,TValue> rhs)
{
return lhs.Self.Subtract( rhs.Value );
}
public static Operable<TImpl,TValue> operator -(Operable<TImpl,TValue> self)
{
return self.Self.Inverse();
}
public static Operable<TImpl,TValue> operator *(Operable<TImpl,TValue> lhs, Operable<TImpl,TValue> rhs)
{
return lhs.Self.Multiply( rhs.Value );
}
public static Operable<TImpl,TValue> operator /(Operable<TImpl,TValue> lhs, Operable<TImpl,TValue> rhs)
{
return lhs.Self.Divide( rhs.Value );
}
public static Operable<TImpl,TValue> operator %(Operable<TImpl,TValue> lhs, Operable<TImpl,TValue> rhs)
{
return lhs.Self.Remainder( rhs.Value );
}
#endregion
#region Operators (Self + TValue)
public static Operable<TImpl,TValue> operator +(Operable<TImpl,TValue> lhs, TValue rhs)
{
TImpl result = lhs.Self.Add( rhs );
return result;
}
public static Operable<TImpl,TValue> operator -(Operable<TImpl,TValue> lhs, TValue rhs)
{
return lhs.Self.Subtract( rhs );
}
public static Operable<TImpl,TValue> operator *(Operable<TImpl,TValue> lhs, TValue rhs)
{
return lhs.Self.Multiply( rhs );
}
public static Operable<TImpl,TValue> operator /(Operable<TImpl,TValue> lhs, TValue rhs)
{
return lhs.Self.Divide( rhs );
}
public static Operable<TImpl,TValue> operator %(Operable<TImpl,TValue> lhs, TValue rhs)
{
return lhs.Self.Remainder( rhs );
}
#endregion
#region Operators (TValue + Self)
public static Operable<TImpl,TValue> operator +(TValue lhs, Operable<TImpl,TValue> rhs)
{
TImpl lhs2 = rhs.Self.CreateFor( lhs );
TImpl result = lhs2.Self.Add( rhs.Value );
return result;
}
public static Operable<TImpl,TValue> operator -(TValue lhs, Operable<TImpl,TValue> rhs)
{
TImpl lhs2 = rhs.Self.CreateFor( lhs );
TImpl result = lhs2.Self.Subtract( rhs.Value );
return result;
}
public static Operable<TImpl,TValue> operator *(TValue lhs, Operable<TImpl,TValue> rhs)
{
TImpl lhs2 = rhs.Self.CreateFor( lhs );
TImpl result = lhs2.Self.Multiply( rhs.Value );
return result;
}
public static Operable<TImpl,TValue> operator /(TValue lhs, Operable<TImpl,TValue> rhs)
{
TImpl lhs2 = rhs.Self.CreateFor( lhs );
TImpl result = lhs2.Self.Divide( rhs.Value );
return result;
}
public static Operable<TImpl,TValue> operator %(TValue lhs, Operable<TImpl,TValue> rhs)
{
TImpl lhs2 = rhs.Self.CreateFor( lhs );
TImpl result = lhs2.Self.Remainder( rhs.Value );
return result;
}
#endregion
public static implicit operator Operable<TImpl,TValue>( TImpl impl )
{
return new Operable<TImpl,TValue>( impl );
}
// public static implicit operator TValue( Operable<TImpl,TValue> self )
// {
// return self.Value;
// }
public static implicit operator TImpl( Operable<TImpl,TValue> self )
{
return self.Self;
}
public Operable( TImpl impl )
{
this.Self = impl;
this.Value = impl.Value;
}
public TImpl Self { get; }
public TValue Value { get; }
}
So for example, supposing we have a custom number type that we want to enforce at compile-time the operators described above...
public struct ComplexNumber
{
public Double Real;
public Double Complex;
}
Simply make it implement IOperable
, so this implementation defines most arithmetic operations on complex numbers:
public struct ComplexNumber : IOperable<ComplexNumber,ComplexNumber>
{
public static implicit operator ComplexNumber( ( Double r, Double i ) valueTuple )
{
return new ComplexNumber( valueTuple.r, valueTuple.i );
}
public Double Real;
public Double Imaginary;
public ComplexNumber( Double real, Double imaginary )
{
this.Real = real;
this.Imaginary = imaginary;
}
public Double Magnitude => Math.Sqrt( ( this.Real * this.Real ) + ( this.Imaginary * this.Imaginary ) );
public ComplexNumber Conjugate => new ComplexNumber( real: this.Real, imaginary: -this.Imaginary );
public ComplexNumber Self => this;
public ComplexNumber Value => this;
public Operable<ComplexNumber,ComplexNumber> Op => new Operable<ComplexNumber,ComplexNumber>( this.Value );
public ComplexNumber Add(ComplexNumber other)
{
Double r = this.Real + other.Real;
Double i = this.Imaginary + other.Imaginary;
return new ComplexNumber( r, i );
}
public ComplexNumber Subtract(ComplexNumber other)
{
Double r = this.Real - other.Real;
Double i = this.Imaginary - other.Imaginary;
return new ComplexNumber( r, i );
}
public ComplexNumber Multiply(ComplexNumber other)
{
// (a+bi) * (c+di) == a(c + di) + bi(c + di)
// == (ac - bd) + (ad + bc)i
Double a = this.Real;
Double b = this.Imaginary;
Double c = other.Real;
Double d = other.Imaginary;
//
Double r = ( a * c ) - ( b * d );
Double i = ( a * d ) + ( b * c );
return new ComplexNumber( r, i );
}
public ComplexNumber Divide(ComplexNumber other)
{
// Division is the same as multiplying by the conjugate.
ComplexNumber conjugate = other.Conjugate;
ComplexNumber numerator = this.Value.Multiply( conjugate );
ComplexNumber denominator = other.Multiply( conjugate );
if( denominator.Imaginary == 0 )
{
Double d = denominator.Real;
Double newReal = numerator.Real / d;
Double newImag = numerator.Imaginary / d;
return new ComplexNumber( newReal, newImag );
}
else
{
throw new NotSupportedException( "Non-trivial complex division is not implemented." );
}
}
public ComplexNumber Remainder(ComplexNumber other)
{
// Remainder isn't the same as Modulo (fun-fact: in C89 the `%` operator is for remainder, not modulo!)
// Anyway, implementing Remainder for complex numbers is non-trivial.
// As is Modulo: https://math.stackexchange.com/questions/274694/modulo-complex-number
// So just throw:
throw new NotSupportedException( "The remainder operation for complex-numbers is not implemented." );
}
public ComplexNumber Inverse()
{
return new ComplexNumber( real: -this.Real, imaginary: -this.Imaginary );
}
#region IEquatable + IComparable
public ComplexNumber CreateFor(ComplexNumber other)
{
return other;
}
public Int32 CompareTo( ComplexNumber other )
{
return this.Magnitude.CompareTo( other.Magnitude );
}
public override Boolean Equals( Object? obj )
{
return obj is ComplexNumber other && this.Equals( other: other );
}
public override Int32 GetHashCode()
{
return base.GetHashCode();
}
public Boolean Equals( ComplexNumber other )
{
return this.Real == other.Real && this.Imaginary == other.Imaginary;
}
public override String ToString()
{
if( this.Imaginary < 0 )
{
return String.Format( CultureInfo.InvariantCulture, "({0}{1}i)", this.Real, this.Imaginary );
}
else
{
return String.Format( CultureInfo.InvariantCulture, "({0}+{1}i)", this.Real, this.Imaginary );
}
}
#endregion
}
So this should be able to be used like so:
public static void Main()
{
ComplexNumber a = ( r: 6, i: 4 );
ComplexNumber b = ( r: 8, i: -2 );
ComplexNumber c = a + b;
Console.WriteLine( "{0} + {1} = {2}", a, b, c );
}
...but it doesn't!
The problem is that we need either a
or b
to be implicitly promoted to Operable<ComplexNumber,ComplexNumber>
so that the overloaded +
operator will be invoked.
The quick-and-dirty workaround is to use that Op
property on the innermost operand (according to operator precedence rules) to trigger the implicit conversion to Operable<>
and the compiler takes care of the rest, including implicit conversion back to ComplexNumber
:
So this:
public static void Main()
{
ComplexNumber a = ( r: 6, i: 4 );
ComplexNumber b = ( r: 8, i: -2 );
ComplexNumber c = a.Op + b;
Console.WriteLine( "{0} + {1} = {2}", a, b, c );
}
...gives me the expected output of (6+4i) + (8--2i) = (14+2i)
.
...which then works with expressions of any length and complexity, just remember to use .Op
on the first operation, not the left-most (in this case, both b.Op
and d.Op
due to them being independent operations:
public static void Main()
{
ComplexNumber a = ( r: 6, i: 4 );
ComplexNumber b = ( r: 8, i: -2 );
ComplexNumber c = ( r: 1, i: 9 );
ComplexNumber d = ( r: 9, i: 5 );
ComplexNumber e = ( r: -2, i: -1 );
ComplexNumber f = a + b.Op * c - ( d.Op / e );
Console.WriteLine( "{0} + {1} * {2} - ( {3} / {4} ) = {5}", a, b, c, d, e, f );
}
Of course, the .Op
part is still an ugly wart, but what can be done about that?
Well, the answer lies in two parts:
IOperable
also has the operators overloaded.
struct Operand
you can still take advantage of overloaded operators (albeit with the .Op
wart)partial
type provided elsewhere, including auto-generating operator implicit
to accommodate any external types that haven't overloaded the operators.Part 1 is straightforward, here's a simple Roslyn analyzer that will raise a warning (or error, at your discretion):
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace You
{
[DiagnosticAnalyzer( LanguageNames.CSharp )]
public class OperatorOverloadingAnalyzer : DiagnosticAnalyzer
{
public static ImmutableArray<String> FixableDiagnosticIds { get; } = ImmutableArray.Create( "0xDEADBEEF0001" );
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => OperatorOverloadingAnalyzerInfo.AsArray;
public override void Initialize( AnalysisContext context )
{
context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.None );
context.EnableConcurrentExecution();
context.RegisterSymbolAction( AnalyzeSymbol, SymbolKind.NamedType );
}
private static void AnalyzeSymbol( SymbolAnalysisContext context )
{
if( IsClassOrStructThatImplementsIOperable( context.Symbol, out INamedTypeSymbol? namedTypeSymbol ) )
{
if( !HasOperators( namedTypeSymbol ) )
{
Diagnostic diagnostic = Diagnostic.Create( OperatorOverloadingAnalyzerInfo.Descriptor, location: namedTypeSymbol.Locations[0], namedTypeSymbol.Name );
context.ReportDiagnostic( diagnostic );
}
}
}
private static Boolean IsClassOrStructThatImplementsIOperable( ISymbol symbol, [NotNullWhen(true)] out INamedTypeSymbol? namedTypeSymbol )
{
if( symbol is INamedTypeSymbol ins )
{
namedTypeSymbol = ins;
if( namedTypeSymbol.TypeKind == TypeKind.Class || namedTypeSymbol.TypeKind == TypeKind.Struct )
{
if( namedTypeSymbol.AllInterfaces.Any( iface => iface.Name == "IOperable" ) )
{
return true;
}
}
return false;
}
else
{
namedTypeSymbol = null;
return false;
}
}
private static readonly HashSet<String> _opMemberNames = new HashSet<String>( StringComparer.Ordinal )
{
"op_Addition",
"op_Division",
"op_Multiply",
"op_Subtraction"
};
private static Boolean HasOperators( INamedTypeSymbol type )
{
Int32 matchCount = 0;
foreach( String memberName in type.MemberNames )
{
if( _opMemberNames.Contains( memberName ) )
{
matchCount++;
}
}
return matchCount == 4;
}
}
}
Just copy and paste the above into the stock Roslyn analyzer project in VS's project templates and off y'go.
Part 2... is too much effort for me now, so consider that an exercise for the reader.
Since its operator can only be overloaded and not overridden its quite difficult. The best solution I can think of is use an abstract class and overloading like this.
public abstract class MyBase
{
public abstract MyBase Add(MyBase right);
public abstract MyBase Subtract(MyBase right);
public static MyBase operator +(MyBase x, MyBase y)
{
//validation here
return x.Add(y);
}
public static MyBase operator -(MyBase x, MyBase y)
{
//validation here
return x.Subtract(y);
}
}
I'd do something like:
public abstract class Scalar<This> where This : Scalar<This>
{
public static This operator +(Scalar<This> a, This b) => a.Add(b);
public abstract This Add(This another);
...
}
Then you can inherit Scalar as:
public sealed class Rational : Scalar<Rational>
{
public override Rational Add(Rational another)
{
...
}
...
}
And that's it:
Rational a = ...;
Rational b = ...;
Rational sum = a + b;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With