假设我有一个接口IFoo,以及一个实现IFoo的类Foo。我想定义自己的逻辑,根据它们包含的数据确定Foo的2个实例是否相等,并重载==& !=与Foo合作时易于使用的运算符。
如果我有2个Foo实例,都存储为Foo,那么这样可以正常工作。但是,如果我有2个实例都存储为IFoo,突然我的重载操作符不再被调用。此外,由于我无法在我的IFoo接口上定义一个运算符,并且我的Foo运算符重载中的至少一个参数必须是Foo类型,我无法通过任何方式看到可以在我的类型上成功地重载操作符。这是对的吗?
此外,任何人都可以清楚为什么会这样吗?通常我会期望我的Foos被存储为IFoos的事实应该与确定调用哪个相等函数无关,因为从根本上它们仍然是Foos!
任何帮助真的很感激
由于
编辑: 好吧,因为我的意思似乎有些混乱,这里有一个例子,希望能够澄清一下:
public interface IFoo
{
}
public class Foo : IFoo
{
public static bool operator==(Foo left, Foo right)
{
....
}
}
Foo foo1 = new Foo();
Foo foo2 = new Foo();
bool comparison1 = foo1 == foo2 //This is successfully calling the overloaded operator
IFoo ifoo1 = foo1;
IFoo ifoo2 = foo2;
bool comparison2 = ifoo1 == ifoo2 //This isn't
答案 0 :(得分:3)
运算符重载仅在编译时存在,编译后只是调用具有已知名称的特定静态方法。
C#编译器识别Foo
的运算符重载,并用a == b
替换Foo.op_Equality(a,b)
。这意味着编译后类Foo中存在非虚拟静态方法op_Equality
。因此,不可能为接口重载操作符 - 接口不能有静态方法。此外,运算符方法非虚拟,这意味着实际方法是从变量类型而不是值类型中解析的,因此编译器将根据a
的变量类型查找可能的运算符重载b
,如果它们都是IFoo
,那么它只能查找op_Equality
类型中的IFoo
- 它不能拥有它。
您只能为基于值类型解析的方法提供自定义逻辑 - 这些是虚拟方法,例如:你可以为Foo类型重写Equals方法,但它对IFoo a == IFoo b
没有帮助,它将被解析为Object.ReferenceEquals(a,b)
答案 1 :(得分:2)
基本上是Aloraman所说的,但让我用另一种方式来看看它是否变得更清晰。
当您将两个变量声明为IFoo,然后将它们实例化为Foo对象时,变量声明定义语法检查中可用的内容。变量声明是供编译器查看的,但实例化在执行之前不会发生,因此编译器无法看到那些实际上是两个Foo对象。因为IFoo没有,并且不能有重载的运算符==,编译器正确地将其视为错误。
当编译器试图找出用于某些操作的内容时,它无法知道这些对象是否真的是Foo,因为在执行期间会分配那些包含Foo对象的IFoo变量。当你比较那些两个IFoo变量时,编译器无法理解你真正想要使用实现IFoo的一个类和重载==。
但是当变量被定义为Foo时,编译器非常清楚在执行时,当它们被实例化时,它们必须被实例化为Foo类型,并且它确切地知道在哪里编译该==运算符。
基本上,编译器在尝试编译该==运算符时,不会查看所有其他代码以查看如何实例化这两个操作数。如果它们被定义为IFoo,那么它就是唯一的信息。