繁体   English   中英

通用函数中的equals()和==

[英]equals() and == in a generic function

我正在对各种类型的集合操作进行比较。

所以我有一个泛型类

 public class Comparer<T, Tid>
...
     public bool Equals(T x, T y)
       {
          var xid = m_idfunc(x);
           var yid = m_idfunc(y);
           return (Tid)xid == (Tid)yid;
       }

其中m_idfunc是传递给Comparer构造函数的lambda,它是

Func<T,Tid>

我用Tid = string创建了一个比较器。 我进入等于函数xid = string1,yid = string2

如果string1和string 2相同(“foo”和“foo”说)

xid == yid

产生错误

(Tid)xid == (Tid)yid

也产生错误(它不应该是必要的 - 我只是变得绝望)

继承我的直接窗口 - 暂停返回xid == yid行

yid.GetType() == typeof(string)
true
xid.GetType() == typeof(string)
true
xid==yid
false
(string)xid==(string)yid
true
xid.Equals(yid)
true

这是怎么回事?

有趣的是,它可能会按照你想要的方式工作。 这是一个例子:

using System;
using System.Text;

namespace ConsoleApplication1 {

    class Program {

        public static void Main()  {
            string myString = "1";
            object objectString = "1";
            string myCopiedString = string.Copy(myString);
            string internedString = string.Intern(myCopiedString);

            Console.WriteLine(myString); //1
            Console.WriteLine(objectString); //1
            Console.WriteLine(myCopiedString); //1
            Console.WriteLine(internedString); //1

            Console.Write(objectString == myString); //true
            Console.Write(objectString == "1"); //true
            Console.Write(objectString == myCopiedString); //!!!FALSE!!!!
            Console.Write(objectString == internedString); //true
            Console.Write(objectString == SomeMethod()); //!!!FALSE!!!
            Console.Write(objectString == SomeOtherMethod()); //true
        }

        public static string SomeMethod() {
            StringBuilder sb = new StringBuilder();
            return sb.Append("1").ToString();
        }

        public static string SomeOtherMethod() {
            return "1".ToString();
        }        
    }
}

可能起作用的原因是由于字符串实习。 所以,这绝对是值得注意的,因为它在测试时实际上可以工作,但是根据实现情况,它可能会突然中断。

在您的情况下,您需要确定您是否关心引用相等或“值”相等。 ==是引用相等,再次,取决于字符串是否被实现可能是真的。 我怀疑你真的想在你的函数中使用EqualityComparer<T>.Default.Equals

如果你在VS中打开它,你会看到编译器警告:“可能的非预期参考比较; 要获得值比较,请将左侧投射到“string”类型。 但是,在你的情况下,编译器不能警告你,因为据它所知,类型是对象,它不知道一个或两个都是字符串。

我最初的假设是,因为它是泛型,它不能在幕后对字符串进行值转换。 我想把一个支持这个的例子放在一起。 :)我必须做一些假设才能将这些东西放在一起,所以我的例子可能不是百分之百。 (我用的代码在底部)

我刚才有时无法编译任何东西

class Comparer<T, TId>
{
    private readonly Func<T, TId> m_idfunc;
    public Comparer(Func<T, TId> idFunc)
    {
        m_idfunc = idFunc;
    }

    public bool Equals(T x, T y)
    {
        var xid = m_idfunc(x);
        var yid = m_idfunc(y);
        return (TId)xid == (TId)yid;
    }
}

我发现https://stackoverflow.com/a/390919/156708并修改了类声明

class Comparer<T, TId> where TId : class 

并编译。 步骤1。

我将Equals函数设置为

public bool Equals(T x, T y)
{
    var xid = m_idfunc(x);
    var yid = m_idfunc(y);
    return (TId)xid == (TId)yid;
}

结果为False (请参阅xid | yid中生成值的完整代码)。 符合我对Generics有所帮助的假设。 还不够,需要看看如果删除了Generics方面会发生什么。

Comparer类更改为

class Comparer<T>
{
    private readonly Func<T, string> m_idfunc;
    public Comparer(Func<T, string> idFunc)
    {
        m_idfunc = idFunc;
    }

    public bool Equals(T x, T y)
    {
        var xid = m_idfunc(x);
        var yid = m_idfunc(y);
        return xid == yid;
    }
}

返回True

我不是100%,但我的假设是基于这样一个事实,即string类的==运算符执行值检查而不是引用检查。 使用泛型时,可能只设置参考检查(没有挖到IL以查看它在那里做了什么),如果内存中的字符串位置不相同,那么它将返回false。 (我有点狡猾的细节,因为我不熟悉它们,只是一个看起来有效的工作假设)

我的完整示例代码包含泛型如下。

using System;
namespace ConsoleApplication1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var compare = new Comparer<Example, string>(example => example.id(example));
            var ex1 = new Example();
            var ex2 = new Example();
            Console.WriteLine(compare.Equals(ex1, ex2));
            Console.ReadLine();
        }
        class Example
        {
            public string id(Example example)
            {
                return new string(new [] {'f', 'o', 'o'});
            }
        }
        class Comparer<T, TId> where TId : class 
        {
            private readonly Func<T, TId> m_idfunc;
            public Comparer(Func<T, TId> idFunc)
            {
                m_idfunc = idFunc;
            }

            public bool Equals(T x, T y)
            {
                var xid = m_idfunc(x);
                var yid = m_idfunc(y);
                return (TId)xid == (TId)yid;
            }
        }
    }
}

希望有所帮助......而且我的理由并非完全错误。 :)

我认为,在Comparer<T, Tid>使用EqualityComparer<TId>更为正确。 此外,我将使用接口来获取标识符,而不是委托:

interface IObjectWithId<T>
{
    T Id { get; }
}

class IdEqualityComparer<T, TId> : EqualityComparer<T>
    where T : IObjectWithId<TId>
{
    public override bool Equals(T x, T y)
    {
        return EqualityComparer<TId>.Default.Equals(x.Id, y.Id);
    }

    public override int GetHashCode(T obj)
    {
        return EqualityComparer<TId>.Default.GetHashCode(obj.Id);
    }
}

class A : IObjectWithId<string>
{
    public string Id { get; set; }
}

用法:

var a = new A { Id = "foo" };
var b = new A { Id = "foo" };
var c = new A { Id = "bar" };

var comparer = new IdEqualityComparer<A, string>();

Console.WriteLine(comparer.Equals(a, b)); // true
Console.WriteLine(comparer.Equals(a, c)); // false

C运算符“==”有两个非常不同的含义。 determine that such a method is applicable to the operand types, or it can perform a reference comparison between the operands, if both operands are known to be reference types and there may exist an object which could be referred to by both operands. 如果编译器可以确定这样的方法适用于操作数类型,它可以调用特定于类型的重载等于运算符方法,或者如果已知两个操作数都是引用类型,它可以执行操作数之间的引用比较并且可能存在可由两个操作数引用的对象。 对于大多数类型,只能进行一种比较; 值类型不支持引用比较,并且大多数引用类型不会重载相等运算符。 但是,有一个公共类可以支持两种类型的比较: System.String

vb.net语言通过仅允许在运算符重载的类型上使用=运算符来避免歧义。 对于参考比较,需要Is运算符。 如果有人试图在vb.net中编写代码,则不允许在类约束泛型上使用=运算符。 可以使用Is运算符,但无论操作数是否= ,它都会检查引用相等性。

实际上,在C#中,假设您的泛型类型有一个类约束(如果没有它, ==运算符将无法工作),编译器只能在泛型类型上使用重载的相等运算符(如果类型约束为一个)操作员过载的。 由于您没有将泛型类型参数约束为string (实际上,因为string是密封的,编译器不允许这样的约束)编译器无法使用相等运算符的string重载。 因此,它使用它知道在类约束泛型上可用的相等运算符的版本 - 引用相等(相当于vb.net中的Is运算符)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM