运算符重载在C#中的接口为基础的编程 [英] Operator Overloading with Interface-Based Programming in C#

查看:86
本文介绍了运算符重载在C#中的接口为基础的编程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我使用的基于接口的编程在当前项目,并已重载运营商(特别是等式和不等式运算符)时遇到了问题。


假设

  • 在我使用C#3.0,.NET 3.5和Visual Studio 2008

更新 - !下面的假设是错误的。

  • 要求所有的比较来使用等号,而不是运营商==是不是一个可行的解决方案,通过你的类型库(如集合)时尤其如此。

使用的原因,我很担心,要求等于,而不是运营商==是,我无法找到它说,它会使用.NET准则的任何地方等号,而不是==操作符,或者甚至建议吧。然而,在重新阅读准则重写equals和运营商== 我发现这一点:

  

在默认情况下,运营商==通过确定两个引用是否表示同一个对象测试引用相等。因此,引用类型没有实现,以获得此功能的==操作符。当一个类型是不可变的,即,包含在该实例中的数据不能被改变,重载operator ==来比较值相等,代替参考平等可以是有用的,因为,如不可变对象,它们可以被认为是相同的,只要因为它们具有相同的值。这不是一个好主意,覆盖运营商==非不可变的类型。

和本 Equatable接口

  测试在这些方法相等时

时,使用泛型集合的IEquatable接口对象,如字典,列表和LinkedList作为含有的IndexOf,LastIndexOf和删除。应该为该可能存储在一个通用集合中的任何物体来​​实现。


约束上

  • 在任何解决方案都不能要求铸造从各自的接口对象的具体类型。


问题

请参阅下面展示的问题code和输出。


问题

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


参考

==操作符(C#参考)

有关predefined值类型,如果它的操作数的值相等,否则为false等号(= =)返回true。对于字符串以外的引用类型,==如果两个操作数指的是同一个对象返回true。对于字符串类型,==比较字符串的值。


另请参见


code

 使用系统;

命名空间OperatorOverloadsWithInterfaces
{
    公共接口IAddress:IEquatable< IAddress>
    {
        字符串StreetName {获得;组; }
        字符串市{获得;组; }
        字符串国{获得;组; }
    }

    公共类地址:IAddress
    {
        私人字符串_streetName;
        私人字符串_City;
        私人字符串_state;

        公共广播(字符串市,字符串状态,串streetName)
        {
            城市=城市,
            国家=状态;
            StreetName = streetName;
        }

        #地区IAddress会员

        公共虚拟字符串StreetName
        {
            {返回_streetName; }
            集合{_streetName =价值; }
        }

        公共虚拟字符串城
        {
            {返回_City; }
            集合{_City =价值; }
        }

        公共虚拟字符串国
        {
            {返回_state; }
            集合{_state =价值; }
        }

        公共静态布尔运算符==(地址LHS,地址RHS)
        {
            Console.WriteLine(地址运算符重载==叫。);
            //如果辩论双方是相同的实例或为空,他们是平等的
            如果(Object.ReferenceEquals(左,右))
            {
                返回true;
            }

            返回lhs.Equals(右);
        }

        公共静态布尔运算符!=(地址LHS,地址RHS)
        {
            !返回(左==右);
        }

        公众覆盖布尔等于(obj对象)
        {
            //使用'为',而不是一个演员拿到一个空,而异常
            //如果对象不是可兑换
            地址地址= OBJ的地址;
            返回this.Equals(地址);
        }

        公众覆盖INT GetHash code()
        {
            串复合= StreetName +城市+状态;
            返回composite.GetHash code();
        }

        #endregion

        #地区IEquatable< IAddress>会员

        公共虚拟布尔等于(IAddress等)
        {
            //每MSDN文档,x.Equals(空)应该返回false
            如果((对象)其他== NULL)
            {
                返回false;
            }

            返回((this.City == other.City)
                &功放;&安培; (this.State == other.State)
                &功放;&安培; (this.StreetName == other.StreetName));
        }

        #endregion
    }

    公共类节目
    {
        静态无效的主要(字串[] args)
        {
            IAddress地址1 =新的地址(西雅图,华盛顿,真棒圣);
            IAddress地址2 =新地址(西雅图,华盛顿,真棒圣);

            functionThatComparesAddresses(地址1,地址);

            Console.Read();
        }

        公共静态无效functionThatComparesAddresses(IAddress地址1,IAddress地址2)
        {
            如果(地址1 ==地址2)
            {
                Console.WriteLine(相等同的接口。);
            }

            如果((地址)地址1 ==地址2)
            {
                Console.WriteLine(平等与左边的演员。);
            }

            如果(地址1 ==(地址)地址2)
            {
                Console.WriteLine(平等与右手边投。);
            }

            如果((地址)地址1 ==(地址)地址2)
            {
                Console.WriteLine(平等双方都投。);
            }
        }
    }
}
 


输出

 地址==操作符重载叫
平等双方施放。
 

解决方案

简短的回答:我觉得你的第二个假设可能存在缺陷。 等于()是正确的方式来检查的语义平等的两个对象,而不是操作符==


龙回答:重载运营商的的在编译的时候进行的,而不是运行时间

除非编译器可以明确地知道对象的它的施加操作者的种类,也不会编译。由于编译器不能确保一个 IAddress 将是东西,有一个覆盖了 == 定义的,它回落到默认 ==操作符实施 System.Object的

要看到这更清楚,请尝试定义一个操作符+ 地址并添加两个 IAddress 实例。除非你明确地转换为地址,它将无法编译。为什么?因为编译器不能告诉一个特定的 IAddress 地址,并没有默认操作符+ 实施回落到 System.Object的


您失望的部分可能是由一个事实,即对象实现了一个 ==操作符,一切是源于对象,所以编译器可以成功解析如 A == b 所有类型的操作。当你推翻了 == ,你希望看到相同的行为,但没有,那是因为最佳匹配的编译器能找到原始的对象的实施。

  

要求所有的比较来使用等号,而不是运营商==是不是一个可行的解决方案,通过你的类型库(如集合)时尤其如此。

在我看来,这是precisely是你应该做的。 等于()是正确的方式来检查的语义平等的两个对象。有时候语义平等只是参考平等在这种情况下,你不需要改变任何东西。在其他情况下,如在你的榜样,你会覆盖等于当你需要一个更强有力的平等契约不是引用相等。例如,你可能要考虑两个相等,如果它们有相同的社会安全号码,或者两个平等如果它们具有相同的VIN

等于() ==操作符是不一样的事情。每当你需要重写 ==操作符,你应该重写等于(),但几乎从来没有周围的其他方法。 ==操作符更多的是语法便利。一些CLR语言(如Visual Basic.NET中),甚至不容许你重写相等操作。

Background

I am using interface-based programming on a current project and have run into a problem when overloading operators (specifically the Equality and Inequality operators).


Assumptions

  • I'm using C# 3.0, .NET 3.5 and Visual Studio 2008

UPDATE - The Following Assumption was False!

  • Requiring all comparisons to use Equals rather than operator== is not a viable solution, especially when passing your types to libraries (such as Collections).

The reason I was concerned about requiring Equals to be used rather than operator== is that I could not find anywhere in the .NET guidelines that it stated it would use Equals rather than operator== or even suggest it. However, after re-reading Guidelines for Overriding Equals and Operator== I have found this:

By default, the operator == tests for reference equality by determining whether two references indicate the same object. Therefore, reference types do not have to implement operator == in order to gain this functionality. When a type is immutable, that is, the data that is contained in the instance cannot be changed, overloading operator == to compare value equality instead of reference equality can be useful because, as immutable objects, they can be considered the same as long as they have the same value. It is not a good idea to override operator == in non-immutable types.

and this Equatable Interface

The IEquatable interface is used by generic collection objects such as Dictionary, List, and LinkedList when testing for equality in such methods as Contains, IndexOf, LastIndexOf, and Remove. It should be implemented for any object that might be stored in a generic collection.


Contraints

  • Any solution must not require casting the objects from their interfaces to their concrete types.


Problem

  • When ever both sides of the operator== are an interface, no operator== overload method signature from the underlying concrete types will match and thus the default Object operator== method will be called.
  • When overloading an operator on a class, at least one of the parameters of the binary operator must be the containing type, otherwise a compiler error is generated (Error BC33021 http://msdn.microsoft.com/en-us/library/watt39ff.aspx)
  • It's not possible to specify implementation on an interface

See Code and Output below demonstrating the issue.


Question

How do you provide proper operator overloads for your classes when using interface-base programming?


References

== Operator (C# Reference)

For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise. For reference types other than string, == returns true if its two operands refer to the same object. For the string type, == compares the values of the strings.


See Also


Code

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.");
            }
        }
    }
}


Output

Address operator== overload called
Equal with both sides cast.

解决方案

Short answer: I think your second assumption may be flawed. Equals() is the right way to check for semantic equality of two objects, not operator ==.


Long answer: Overload resolution for operators is performed at compile time, not run time.

Unless the compiler can definitively know the types of the objects it's applying an operator to, it won't compile. Since the compiler cannot be sure that an IAddress is going to be something that has an override for == defined, it falls back to the default operator == implementation of System.Object.

To see this more clearly, try defining an operator + for Address and adding two IAddress instances. Unless you explicitly cast to Address, it will fail to compile. Why? Because the compiler can't tell that a particular IAddress is an Address, and there is no default operator + implementation to fall back to in System.Object.


Part of your frustration probably stems from the fact that Object implements an operator ==, and everything is an Object, so the compiler can successfully resolve operations like a == b for all types. When you overrode ==, you expected to see the same behavior but didn't, and that's because the best match the compiler can find is the original Object implementation.

Requiring all comparisons to use Equals rather than operator== is not a viable solution, especially when passing your types to libraries (such as Collections).

In my view, this is precisely what you should be doing. Equals() is the right way to check for semantic equality of two objects. Sometimes semantic equality is just reference equality, in which case you won't need to change anything. In other cases, as in your example, you'll override Equals when you need a stronger equality contract than reference equality. For example, you may want to consider two Persons equal if they have the same Social Security number, or two Vehicles equal if they have the same VIN.

But Equals() and operator == are not the same thing. Whenever you need to override operator ==, you should override Equals(), but almost never the other way around. operator == is more of a syntactical convenience. Some CLR languages (e.g. Visual Basic.NET) don't even permit you to override the equality operator.

这篇关于运算符重载在C#中的接口为基础的编程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆