为何IBitwiseOperators不允许泛型函数使用短路运算符?
问题背景
研究向System.Boolean添加新System.Numerics接口的.NET API提案时,发现短路运算符&&和||需要实现true和false运算符,于是基于.NET 7的静态抽象运算符特性做测试验证。
测试代码定义
自定义真值运算符接口
public interface ITruthOperators<TSelf> where TSelf : ITruthOperators<TSelf>? { static abstract bool operator true(TSelf left); static abstract bool operator false(TSelf left); static abstract TSelf operator !(TSelf left); static abstract TSelf TruthyIdentity { get; } static abstract TSelf FalseyIdentity { get; } }
自定义布尔类型
实现上述接口与IBitwiseOperators<MyBool, MyBool, MyBool>:
public struct MyBool(bool val) : ITruthOperators<MyBool>, IBitwiseOperators<MyBool, MyBool, MyBool> { public bool Value {get;} = val; public static bool operator true(MyBool b) => b.Value; public static bool operator false(MyBool b) => !b.Value; public static MyBool operator !(MyBool b) => new(!b.Value); public static MyBool TruthyIdentity => new(true); public static MyBool FalseyIdentity => new(false); public static MyBool operator &(MyBool a, MyBool b) => new(a.Value & b.Value); public static MyBool operator |(MyBool a, MyBool b) => new(a.Value | b.Value); public static MyBool operator ^(MyBool a, MyBool b) => new(a.Value ^ b.Value); public static MyBool operator ~(MyBool val) => new(!val.Value); }
问题现象
此时可以正常编写if (T.TruthyIdentity)、if (!someMyBool)这类代码,也能使用非短路运算符someMyBool & someOtherMyBool,但短路运算符无法使用。
编写如下泛型函数:
public T IsTrue<T>(T a, T b) where T : ITruthOperators<T>, IBitwiseOperators<T, T, T> { return a && b; }
会触发错误:
要作为短路运算符适用,用户定义的逻辑运算符('IBitwiseOperators<T, T, T>.operator &(T, T)')必须具有相同的返回类型和参数类型
但实际上,当IBitwiseOperators<TSelf, TOther, TResult>实例化为IBitwiseOperators<MyBool, MyBool, MyBool>时,operator &的参数和返回类型均为MyBool,完全符合要求。
排查尝试
重新定义仅包含同类型参数与返回值的自定义位运算符接口:
public interface IBitwiseOperators<TSelf> where TSelf : IBitwiseOperators<TSelf>? { static abstract TSelf operator &(TSelf left, TSelf right); static abstract TSelf operator |(TSelf left, TSelf right); static abstract TSelf operator ^(TSelf left, TSelf right); static abstract TSelf operator ~(TSelf value); }
将泛型函数约束改为where T : ITruthOperators<T>, IBitwiseOperators<T>后,又出现新错误:
要使'IBitwiseOperators
.operator &(T, T)'可用作短路运算符,其声明类型'IBitwiseOperators '必须定义true和false运算符
明明T同时约束了两个接口,ITruthOperators<T>已定义true/false运算符,该错误不符合预期。
目前唯一可行的方式是将true/false运算符与位运算符放在同一个接口中。
C#规范参考
根据C#规范中关于短路运算符的定义:
当
&&或||的操作数属于声明了适用用户定义运算符&或|的类型时,以下两个条件必须同时成立,其中T是所选运算符的声明类型:
- 所选运算符的返回类型和每个参数的类型都必须是
T。即运算符应计算两个T类型操作数的逻辑AND或OR,并返回T类型的结果。T必须包含true和false运算符的声明。
如果任一要求未满足,则会发生绑定时代错误。否则,&&或||操作通过将用户定义的true或false运算符与所选用户定义运算符结合来计算。
这里的疑问是:“所选运算符的声明类型”指的是接口还是泛型类型?按逻辑应该是泛型类型T,因为我们处理的是约束到这些接口的泛型类型,变量实际类型为T。
核心问题
这种编译器行为是设计如此,还是属于bug?
内容的提问来源于stack exchange,提问作者Charlieface

