代码之家  ›  专栏  ›  技术社区  ›  Zach Burlingame

C语言中基于接口编程的运算符重载#

  •  70
  • Zach Burlingame  · 技术社区  · 17 年前


    • 我正在使用C#3.0、.NET3.5和VisualStudio2008

    Guidelines for Overriding Equals and Operator== 我发现:

    还有这个 Equatable Interface


    违禁品

    • 任何解决方案都不能要求将对象从其接口转换为其具体类型。


    问题

    在使用接口基编程时,如何为类提供适当的运算符重载?


    工具书类

    == Operator (C# Reference)


    另见


    密码

    using System;
    
    namespace OperatorOverloadsWithInterfaces
    {
        public interface IAddress : IEquatable<IAddress>
        {
            string StreetName { get; set; }
            string City { get; set; }
            string State { get; set; }
        }
    
        public class Address : IAddress
        {
            private string _streetName;
            private string _city;
            private string _state;
    
            public Address(string city, string state, string streetName)
            {
                City = city;
                State = state;
                StreetName = streetName;
            }
    
            #region IAddress Members
    
            public virtual string StreetName
            {
                get { return _streetName; }
                set { _streetName = value; }
            }
    
            public virtual string City
            {
                get { return _city; }
                set { _city = value; }
            }
    
            public virtual string State
            {
                get { return _state; }
                set { _state = value; }
            }
    
            public static bool operator ==(Address lhs, Address rhs)
            {
                Console.WriteLine("Address operator== overload called.");
                // If both sides of the argument are the same instance or null, they are equal
                if (Object.ReferenceEquals(lhs, rhs))
                {
                    return true;
                }
    
                return lhs.Equals(rhs);
            }
    
            public static bool operator !=(Address lhs, Address rhs)
            {
                return !(lhs == rhs);
            }
    
            public override bool Equals(object obj)
            {
                // Use 'as' rather than a cast to get a null rather an exception
                // if the object isn't convertible
                Address address = obj as Address;
                return this.Equals(address);
            }
    
            public override int GetHashCode()
            {
                string composite = StreetName + City + State;
                return composite.GetHashCode();
            }
    
            #endregion
    
            #region IEquatable<IAddress> Members
    
            public virtual bool Equals(IAddress other)
            {
                // Per MSDN documentation, x.Equals(null) should return false
                if ((object)other == null)
                {
                    return false;
                }
    
                return ((this.City == other.City)
                    && (this.State == other.State)
                    && (this.StreetName == other.StreetName));
            }
    
            #endregion
        }
    
        public class Program
        {
            static void Main(string[] args)
            {
                IAddress address1 = new Address("seattle", "washington", "Awesome St");
                IAddress address2 = new Address("seattle", "washington", "Awesome St");
    
                functionThatComparesAddresses(address1, address2);
    
                Console.Read();
            }
    
            public static void functionThatComparesAddresses(IAddress address1, IAddress address2)
            {
                if (address1 == address2)
                {
                    Console.WriteLine("Equal with the interfaces.");
                }
    
                if ((Address)address1 == address2)
                {
                    Console.WriteLine("Equal with Left-hand side cast.");
                }
    
                if (address1 == (Address)address2)
                {
                    Console.WriteLine("Equal with Right-hand side cast.");
                }
    
                if ((Address)address1 == (Address)address2)
                {
                    Console.WriteLine("Equal with both sides cast.");
                }
            }
        }
    }
    

    Address operator== overload called
    Equal with both sides cast.
    
    2 回复  |  直到 17 年前
        1
  •  60
  •   John Feminella    17 年前

    简短回答:我认为你的第二个假设可能有缺陷。 Equals() 语义平等 两个对象,而不是 operator == .


    在编译时执行,而不是在运行时执行

    除非编译器能够明确地知道它应用运算符的对象的类型,否则它不会编译。因为编译器无法确保 IAddress == 如果已定义,则返回默认值 接线员== 实施 System.Object .

    要更清楚地看到这一点,请尝试定义 operator + 对于 Address 加上两个 实例。 除非您明确地强制转换为 ,它将无法编译。为什么?因为编译器无法识别特定的 女装 是一个 ,并且没有默认值 接线员+ 系统对象 .


    Object 实施 接线员== 对象 ,因此编译器可以成功解析以下操作 a == b 对象 实施

    在我看来,这正是你应该做的。 等于() 检查是否存在错误的正确方法是什么 语义平等 指两个物体。 Equals 当您需要一个比引用平等更强大的平等契约时。例如,您可能需要考虑两个问题。 Persons 如果他们有相同或两个社会保险号码,则相等 Vehicles

    但是 等于() 接线员== 接线员== ,您应该覆盖 等于() ,但几乎没有相反的方式。 接线员==

        2
  •  4
  •   Pang Ajmal PraveeN    5 年前

    我们遇到了同样的问题,并找到了一个很好的解决方案:ReSharper定制模式。

    我们将所有用户配置为除了使用自己的全局模式目录之外,还使用一个通用的全局模式目录,并将其放入SVN中,以便每个人都可以对其进行版本控制和更新。

    目录包括我们系统中已知错误的所有模式:

    $i1$ == $i2$ (其中i1和i2是 我们的接口类型,或派生的。

    替换模式是

    $i1$.Equals($i2$)

    严重性为“显示为错误”。

    同样地,我们也有 $i1$ != $i2$

    希望这有帮助。 P.S.全球目录是ReSharper 6.1(EAP)中的功能,将很快标记为最终目录。

    使现代化 ReSharper Issue 若要将所有接口“==”标记为警告,除非它与null进行比较。请投票,如果你认为这是一个值得的功能。

    更新2 :ReSharper还具有[CannotApplyEqualityOperator]属性,可以提供帮助。

        3
  •  0
  •   kofifus    5 年前

    在我看来,这是C#中令人困惑的设计缺陷。IMO==应该和现在完全一样 Equals 等于 )如果您想要只引用相等,那么您可以调用一个专门的方法,比如ReferenceEquals。这是由围绕运算符重载和继承的语言设计缺陷造成的,即您注意到的缺陷和缺少对运算符的扩展方法支持。