我的Term
类中定义了以下运算符重载:
public static Term operator *(int c, Term t) {...}
此类还定义了从Variable
到Term
的隐式转换:
public static implicit operator Term(Variable var) {...}
我想了解为什么以下内容无法编译:
static void Main(string[] args)
{
Variable var = ...; // the details don't matter
Console.WriteLine(2 * var); // var isn't implicitly converted to Term...
Console.ReadKey();
}
编译器说:
运营商' *'不能应用于' int'类型的操作数和' OOSnake.Variable'
为什么找不到operator *
的重载?
编辑:根据评论中的建议,这是一个重新生成错误的小完整示例:
namespace Temp
{
class A {
}
class B
{
public static implicit operator B(A a) { return new B(); }
public static B operator *(int c, B b) { return new B(); }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(2 * new A());
}
}
}
答案 0 :(得分:4)
基本上,运算符重载决策不包括隐式的用户定义转换,以便找到可能适用的运算符。
来自C#5规范的第7.3.4节:
x op y
形式的操作,其中op
是一个可重载的二元运算符,x
是X
类型的表达式,y
是一个类型Y
的表达式按如下方式处理:
- 确定由
X
和Y
为操作运算符op(x, y)
提供的候选用户定义运算符集。该集合由X提供的候选运算符与Y
提供的候选运算符的并集组成,每个运算符使用第7.3.5节的规则确定。如果X
和Y
属于同一类型,或者X
和Y
是从公共基类型派生的,则共享候选运算符仅出现在组合集中一次。
7.3.5在搜索一组运算符时不包含隐式的用户定义转换。
请注意,如果在Term
类中声明了对Variable
的隐式转换,这也不起作用 - 尽管指定和实现更合理,因为编译器可以查看从操作数类型到其他类型的转换集,并使用它们进行重载解析。
然而,这只是寻找运营商的问题。编译器 很高兴在考虑是否适用过载时执行隐式转换。例如,在您的情况下,如果您添加:
class A
{
public static B operator *(A a, B b) { return new B(); }
}
然后这是有效的:
A a = new A();
Console.WriteLine(a * a);
答案 1 :(得分:1)
Console.WriteLine(2 * var)不包含您希望将var转换为Term类型的任何提示。编译器会看到int和multiplication运算符以及“Variable”类型的变量。
编辑:为了澄清,为了使您的示例正常工作,编译器必须通过所有范围内的类型,看看是否恰好有一个类型'A'的隐式转换
如果还发生了类似的C类:
class C
{
public static implicit operator C(A a) { return new A(); }
public static B operator *(int i, C c) { return new C(); }
}
不知道会发生什么。
这就是编译器不这样做的原因:)
答案 2 :(得分:0)
如果你想让你的例子起作用,你必须将将A转换为B的隐式运算符移动到A类,如下所示:
namespace Temp
{
class A
{
public static implicit operator B(A a) { return new B(); }
}
class B
{
public static B operator *(int c, B b) { return new B(); }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(2 * ((B)new A()));
}
}
}
但你不能使用这个2 * new A()
,因为(Jon Skeet回答):
基本上,运算符重载决策不包括隐式的用户定义转换,以便找到可能适用的运算符。