我需要定义一个接口,它必须强制执行某些运算符重载到实现它的类型。似乎没有一种明显的方法可以做到这一点,因为运算符重载必须在类中使用静态方法完成。有没有办法达到相同的效果(使用抽象类或其他任何东西)?
答案 0 :(得分:23)
有点黑客,但是......
您可以在基类中提供运算符重载,然后在其中一个类中调用一些已发布的抽象方法来完成该任务。
public abstract class MyClass
{
public static MyClass operator +(MyClass c1, MyClass c2)
{
return c1.__DoAddition(c2);
}
protected abstract MyClass __DoAddition(MyClass c2);
}
答案 1 :(得分:8)
没有。唯一明智的方法是使用单元测试检查使用反射来查找所有具体实现,然后验证这种情况。您也可以 在运行时通过静态构造函数执行相同操作,但问题是哪个静态构造函数?
另一种方法是删除运算符并使用基于接口的方法;例如,如果您需要T
拥有+(T,T)
,则代替运算符的接口使用Add(T)
方法。这里的另一个优点是接口可用于泛型(通常通过约束),其中 - 使用来自通用代码的运算符需要付出一些努力。
答案 2 :(得分:5)
您可以在抽象基类中实现重载,但是将实际操作细节委托给抽象方法。然后,这将必须实施,重载将与他们的实现相结合。
public abstract class OverLoadingBase
{
public abstract OverLoadingBase DoAdd(OverLoadingBase y);
public static OverLoadingBase operator +(OverLoadingBase x, OverLoadingBase y)
{
return x.DoAdd(y);
}
}
虽然我不确定这是否完整。
答案 3 :(得分:4)
我过去做过这个......
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);
}
然后实施如下:
public class ConcreteClass : BaseClass<ConcreteClass>
{
protected ConcreteClass DoAddition(ConcreteClass c2)
{
...
}
}
答案 4 :(得分:1)
由于它的操作符只能被重载而不能被覆盖,因此非常困难。我能想到的最好的解决方案是使用抽象类并像这样重载。
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);
}
}
答案 5 :(得分:0)
我会做类似的事情:
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);
...
}
然后,您可以将Scalar继承为:
public sealed class Rational : Scalar<Rational>
{
public override Rational Add(Rational another)
{
...
}
...
}
就是这样:
Rational a = ...;
Rational b = ...;
Rational sum = a + b;
答案 6 :(得分:0)
在 C# 中是否有任何方法可以在派生类中强制执行运算符重载?
严格来说,类型不是从 interface
“派生”的,它们只是 implement
它 - 但是如果您指的是在派生的 class
中要求运算符重载从父 class
然后可以通过使父 class
泛型以允许变体运算符参数和返回类型来完成,但这实际上意味着子类不能再次被子类化(不使子类通用)。
有没有办法达到同样的效果(使用抽象类或其他任何东西)?
现在,假设您指的是仅接口,那么是,这是可能的!诀窍是不要在接口或类上定义运算符,而是在包装器-struct
中定义运算符 - 通过 operator implicit
...< /p>
虽然有一个小问题,但是如果您使用的是 C# 6.0 或更高版本,这是 完全可以解决的...继续阅读!
您所描述的是(另一个)很好的用例,适用于开放通用接口上的包装器struct
。
class
) 或值 (struct
),包括任何 interface
,包装在 struct
中,对您的程序不可见,然后该包装结构会添加您需要的功能。struct
等)在 .NET 中是“免费的”,因为它们不会导致 GC 堆分配。要完成这项工作,首先要定义带有您要支持的操作的接口。
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
方法,它们都接受并返回相同的 Operable<TImpl,TValue>
。struct Operable
还定义了 implicit operator
用于隐式转换 to-and-from TImpl
和 to TValue
,这有助于使这种方法可用。 TValue
是不可能的,因为无法知道 TImpl
是什么,但是您可以在 struct Operable<>
上定义运算符,允许任何一个操作数的原始 TValue
,这样它就可以从另一个操作数推断 TImpl
。// 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; }
}
例如,假设我们有一个自定义数字类型,我们希望在编译时强制执行上述运算符...
public struct ComplexNumber
{
public Double Real;
public Double Complex;
}
简单地让它实现IOperable
,所以这个实现定义了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
}
所以这应该可以像这样使用:
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 );
}
...但它没有!
问题是我们需要将 a
或 b
隐式提升到 Operable<ComplexNumber,ComplexNumber>
以便重载的 +
运算符将是调用。
quick-and-dirty 解决方法是使用最内层操作数上的 Op
属性(根据 operator precedence rules)触发隐式转换为 {{1} } 并且编译器负责其余的工作,包括隐式转换回 Operable<>
:
所以:
ComplexNumber
...给了我 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 );
}
的预期输出。
...然后可以处理任何长度和复杂度的表达式,只需记住在 first 操作上使用 (6+4i) + (8--2i) = (14+2i)
,而不是最左边的(在这种情况下,两个 { {1}} 和 .Op
,因为它们是独立的操作:
b.Op
当然,d.Op
部分仍然是一个丑陋的疣,但有什么办法呢?
嗯,答案分为两部分:
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 );
}
的任何类型是否也重载了运算符。
.Op
您仍然可以利用重载运算符(尽管带有 IOperable
疣)struct Operand
类型自动生成必要的运算符,包括自动生成 .Op
以适应没有重载运算符的任何外部类型。立>
第 1 部分很简单,这是一个简单的 Roslyn 分析器,它会发出警告(或错误,由您自行决定):
partial
只需将上述内容复制并粘贴到 VS 项目模板中的 Roslyn 分析器项目中即可。
第 2 部分......现在对我来说太费力了,所以把它当作读者的练习。