背景

我使用的接口编程上目前项目,并已遇到一个问题时,载运营商(具体的平等和不平等的经营者).


假设

  • 我使用的C#3.0,.净3.5和Visual Studio2008

更新以下的假设是假的!

  • 要求所有的比较使用的平等,而不是操作者==不是一种可行的解决方案,特别是当通过你的类型文库(诸如集的).

我之所以感到关切的是关于需要等于将使用,而不是操作者==的是,我找不到任何地方。净的准则,它指出,它将使用等,而不是操作者==或者甚至建议。然而,在重新阅读 准则》压倒一切的等和操作人员== 我发现了这个:

默认情况下,操作者==试验供参考的平等,通过确定是否两个参考文献表示同样的对象。因此,参照类型没有实施操作员==为了获得这一功能。当一个类型是不可改变的,也就是说,数据中包含的实例不能更改,载操作员==比较价值的平等,而不是参照平等可能是有用的,因为不可改变的对象,他们可以被认为是相同的,只要他们有同样的价值。它不是一个好主意,以复盖操作者==非不可改变类型。

和这个 Equatable口

这就不能修改的接口是使用通用收集对象,例如字典、列表和链表进行测试时,为女平等等方法包含个,LastIndexOf,并删除。它应该是实现任何目的,可能被存储在一个通用的集合。


约束

  • 任何解决方案必须不需要铸造的对象,他们的接口到他们的具体类型。

的问题

  • 当双方的操作人员==是一个界面,没有运营商==超载方法的签名从基础的具体类型的匹配,从而默认对象操作员==的方法将被称为。
  • 当载有操作上的一类,至少有一个参数的二进制的操作员必须包含类型,否则一个编译器产生错误(错误BC33021 http://msdn.microsoft.com/en-us/library/watt39ff.aspx)
  • 它不可能指定的执行上的一个接口

看到码和输出下面表明这一问题。


的问题

你怎么提供适当操作过载于您的课程时使用的接口基础编程?


参考文献

==Operator(C#参考)

对于预先定义的价值的类型、平等操作员(==)返回真正的如果它的操作数都是平等的,假。为参照其他类型比string,==返回真正的如果它的两个操作数指相同的对象。为串的类型,==比较的价值观。


也参看


代码

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.
有帮助吗?

解决方案

简短的回答:我觉得你的第二个假设可能是有缺陷的。 Equals() 是正确的方式来检查 语义上的平等 两个对象,不 operator ==.


只要回答:过载决议为运营商是 进行汇编时,无法运行时间.

除非编译器可以明确地知道类型的对象是施加操作者,它不会编译。由于编译器不能确保一个 IAddress 要的东西,都有一个复盖 == 定义,回落到默认的 operator == 执行情况 System.Object.

看看这个更清楚,试图限定一个 operator + 对于 Address 并添加两个 IAddress 实例。 除非你明确地浇铸 Address, 它将无法编译。为什么?因为编译器不能告诉一个特别的 IAddress 是一个 Address, 和没有默认的 operator + 实现的回落到在 System.Object.


一部分的你的挫折可能源自这一事实, Object 实现一个 operator ==, 和一切的是 Object, ,因此编译器可以成功地解决操作的喜欢 a == b 对于所有种类型。当你推翻了 ==, 你期望看到的同样的行为,但不,这是因为最好的匹配的编译器可以找到是原来的 Object 执行情况。

要求所有的比较使用的平等,而不是操作者==不是一种可行的解决方案,特别是当通过你的类型文库(诸如集的).

在我看来,这正是你应该做的。 Equals() 是正确的方式来检查 语义上的平等 两个对象。 有时候,语义上的平等只是参考平等,在这种情况下你不需要改变任何东西。在其他情况下,如在例,只复盖 Equals 当你需要一个更强大的平等,合同于参照的平等。例如,你可能想要考虑两个 Persons 等如果他们有相同的社会安全号码,或者两个 Vehicles 等如果他们有相同的VIN。

Equals()operator == 是不一样的东西。每当你需要复盖 operator ==, 你应该复盖 Equals(), 但几乎没有其他方式。 operator == 更多的是语法的便利。一些CLR语言(例如视觉Basic.NET)甚至不允许您替代的平等操作员。

其他提示

我们碰到了同样的问题,并发现一个很好的解决方案:Resharper定义的模式。

我们构成我们所有的用户使用全球共同模式编录在除了他们自己的,把它放到SVN,以便它可以进行版本控制和更新每个人。

目录中包含的所有模式称为是错误的,在我们的系统:

$i1$ == $i2$ (其中i1和i2是 表情 我们的界面类型,或者衍生的。

替换模式

$i1$.Equals($i2$)

和严重程度是"显示为错误"。

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

希望这会有所帮助。P.S.全球目录的特点是在Resharper6.1(EAP),将被标记为最后的很快。

更新:我申请了 Resharper问题 标记所有接口'=='a警告,除非它是比较为空。请投票,如果你认为这是一个有价值的特征。

Update2:Resharper还有[CannotApplyEqualityOperator]属性,可以帮助。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top