在编写C#时,我倾向于编写很多实现IEquatable<T>
、IComparable<T>
或两者都实现的值类型对象.
对于这个提议,让我们假设我正在编写一个名为Int256
的虚构 struct ,该 struct 具有相等和可比较的值语义;例如:
public readonly struct Int256 : IEquatable<Int256>, IComparable<Int256>, IComparable
{
public bool Equals(Int256 other)
{
// TODO : is this equal to other?
}
public int CompareTo(Int256 other)
{
// TODO : how does this compare to other?
}
public int CompareTo(object? obj)
{
if (obj is null) return 1;
if (obj is not Int256 int256) throw new ArgumentException("Obj must be of type Int256.");
return CompareTo(int256);
}
public static bool operator ==(Int256 left, Int256 right)
{
return Equals(left, right);
}
public static bool operator !=(Int256 left, Int256 right)
{
return !Equals(left, right);
}
public static bool operator >(Int256 left, Int256 right)
{
return left.CompareTo(right) is 1;
}
public static bool operator >=(Int256 left, Int256 right)
{
return left.CompareTo(right) is 1 or 0;
}
public static bool operator <(Int256 left, Int256 right)
{
return left.CompareTo(right) is -1;
}
public static bool operator <=(Int256 left, Int256 right)
{
return left.CompareTo(right) is -1 or 0;
}
}
我们还假设我要创建一些具有相同语义的其他虚构的 struct ;例如,UInt256
和Decimal256
.虽然这些运算符很简单,但对于每个值类型对象来说,实现这些运算符变得单调乏味.
最近,我一直在研究C#11的新语言特性,特别是静态接口方法,我认为这在很大程度上使新的通用数学接口成为可能.考虑到这一点,我可以在我的实现中添加一些额外的接口,特别是IEqualityOperators<TSelf, TOther, TResult>
和IComparisonOperators<TSelf, TOther, TResult>
;例如:
public readonly struct Int256 : IEquatable<Int256>, IComparable<Int256>, IComparable, IComparisonOperators<Int256, Int256, bool>
{
...
}
一百零二
归根结底,这并不能真正解决问题.所有这些接口所做的就是确保操作符的实现.
我的建议是,是否可以将接口设计为与IEquatable<T>
和IComparable<T>
相关的典型样板代码的auto-implement some,特别是运算符:==
、!=
、>
、>=
、<
、<=
;例如:
IAutoEquatable<T>个
public interface IAutoEquatable<T> : IEquatable<T>, IEqualityOperators<T, T, bool> where T : IAutoEquatable<T>
{
// Auto-implemented boilerplate.
static virtual bool operator ==(T? left, T? right)
{
return Equals(left, right);
}
// Auto-implemented boilerplate.
static virtual bool operator !=(T? left, T? right)
{
return !Equals(left, right);
}
}
IAutoComparable<T>个
public interface IAutoComparable<T> : IComparable<T>, IComparable, IComparisonOperators<T, T, bool> where T : IAutoComparable<T>
{
// Auto-implemented boilerplate.
static virtual bool operator ==(T? left, T? right)
{
return Equals(left, right);
}
// Auto-implemented boilerplate.
static virtual bool operator !=(T? left, T? right)
{
return !Equals(left, right);
}
// Auto-implemented boilerplate.
static virtual bool operator >(T left, T right)
{
return left.CompareTo(right) is 1;
}
// Auto-implemented boilerplate.
static virtual bool operator >=(T left, T right)
{
return left.CompareTo(right) is 1 or 0;
}
// Auto-implemented boilerplate.
static virtual bool operator <(T left, T right)
{
return left.CompareTo(right) is -1;
}
// Auto-implemented boilerplate.
static virtual bool operator <=(T left, T right)
{
return left.CompareTo(right) is -1 or 0;
}
}
这里的目的是实现者只需要分别实现bool Equals(T other)
和int CompareTo(T other)
,但是,鉴于操作符是在接口上实现的,他们免费获得操作符!
给出我的Int256
个例子,它可能如下所示:
public readonly struct Int256 : IEquatable<Int256>, IComparable<Int256>, IComparable
{
public bool Equals(Int256 other)
{
// TODO : is this equal to other?
}
public int CompareTo(Int256 other)
{
// TODO : how does this compare to other?
}
public int CompareTo(object? obj)
{
if (obj is null) return 1;
if (obj is not Int256 int256) throw new ArgumentException("Obj must be of type Int256.");
return CompareTo(int256);
}
}
但我仍然可以对其使用运算符;例如:
Int256 a = 123;
Int256 b = 456;
a == b; // False
a != b; // True
a > b; // False
a >= b; // False
a < b; // True
a <= b; // True
然而,这其中存在一个问题.
虽然这些接口IAutoEquatable<T>
和IAutoComparable<T>
包含操作符的实现,但我仍然希望在Int256
中实现它们.
Questions个
- 为什么接口中的
virtual
个默认实现仍然需要实现?也就是说,为什么Int256
不直接使用默认实现? - future 的C#版本是否有可能解决这个问题,这样我们就可以使用它来减少编写样板代码的需要?
在这里与C#语言设计团队一起长大:https://github.com/dotnet/csharplang/discussions/7032