我正在对各种类型的集合操作进行比较。
所以我有一个通用类
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
怎么回事?
答案 0 :(得分:3)
有趣的是,可能按照您希望的方式工作。这是一个例子:
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”类型。但是,在你的情况下,编译器不能警告你,因为据它所知,类型是对象,它不知道一个或两个都是字符串。
答案 1 :(得分:1)
我最初的假设是,因为它是泛型,所以它不能在幕后对字符串进行值转换。我想把一个支持这个的例子放在一起。 :) 我必须做一些假设才能将这些东西放在一起,所以我的例子可能不是百分之百。 (我使用的代码位于底部)
当我刚刚
时,我无法得到任何东西来编译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
。
我不是百分之百,但我的假设是基于==
类的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;
}
}
}
}
希望有所帮助......而且我的推理并不是非常错误。 :)
答案 2 :(得分:0)
我认为,在EqualityComparer<TId>
内使用Comparer<T, Tid>
更为正确。此外,我将使用interface来获取标识符,而不是委托:
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
答案 3 :(得分:0)
C运算符“==”有两个非常不同的含义。如果编译器可以静态地确定这样的方法适用于操作数类型,它可以调用特定于类型的重载等于运算符方法,或者它可以在操作数之间执行引用比较,如果两者都可以已知操作数是引用类型,并且可能存在可由两个操作数引用的对象。对于大多数类型,只能进行一种比较;值类型不支持引用比较,并且大多数引用类型不会重载相等运算符。但是,有一个共同的类可以支持两种类型的比较:System.String
。
vb.net语言通过仅允许=
运算符用于超载它的类型来避免歧义。对于参考比较,需要Is
运算符。如果有人试图在vb.net中编写代码,那么=
运算符将不允许使用类约束泛型。可以使用Is
运算符,但无论操作数是否超载=
,它都会检查引用相等。
实际上,在C#中,假设你的泛型类型有一个类约束(如果没有它,==
运算符将不起作用),编译器只能在泛型类型上使用重载的相等运算符。类型被约束为运算符过载的类型。由于您没有将泛型类型参数约束为string
(实际上,因为string
已被密封,编译器将不允许这样的约束)编译器无法使用{{1}等于运算符的重载。因此,它使用它知道在类约束泛型上可用的相等运算符的版本 - 引用相等(相当于vb.net中的string
运算符)。